Compare commits
364 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7d14e60c24 | ||
|
|
4e1393e166 | ||
|
|
c6b9a9c948 | ||
|
|
09427ff5e8 | ||
|
|
3c7a2bc962 | ||
|
|
e00858fd73 | ||
|
|
594ba29e56 | ||
|
|
c614db01b7 | ||
|
|
8257b9634d | ||
|
|
6d624ae33b | ||
|
|
2cb6cf3a4d | ||
|
|
c90c95fc12 | ||
|
|
a88063aa5c | ||
|
|
d655ad283b | ||
|
|
0e66afa78b | ||
|
|
fd527fb30a | ||
|
|
d6d837928f | ||
|
|
a28c8a3acb | ||
|
|
4311c04634 | ||
|
|
5ff3c358ed | ||
|
|
880fa699ef | ||
|
|
bd7c7cd91d | ||
|
|
fb2e7c71e1 | ||
|
|
923407e77f | ||
|
|
2181258e38 | ||
|
|
015481bd3e | ||
|
|
27dada90d6 | ||
|
|
de2d068bb6 | ||
|
|
fd34ea0a78 | ||
|
|
ad02f98858 | ||
|
|
27679a6c58 | ||
|
|
592ebdf555 | ||
|
|
35101841cc | ||
|
|
9033fd67d7 | ||
|
|
066db7bf04 | ||
|
|
c64c188ae5 | ||
|
|
8889ced3c8 | ||
|
|
c4bf74bdf5 | ||
|
|
090a761aef | ||
|
|
11d6685c45 | ||
|
|
45934d8a59 | ||
|
|
c9de739331 | ||
|
|
c78bc602b0 | ||
|
|
f78ac15820 | ||
|
|
0e05030047 | ||
|
|
46869e4df4 | ||
|
|
8ba8ae4f6c | ||
|
|
03e939a49b | ||
|
|
f60281688e | ||
|
|
c3a23b7a1b | ||
|
|
1941fc1675 | ||
|
|
167319022f | ||
|
|
fb1d17764a | ||
|
|
5a20b85759 | ||
|
|
d4668af82f | ||
|
|
ce8a34b1bb | ||
|
|
f9852fb553 | ||
|
|
3957d487c3 | ||
|
|
6427e0d84b | ||
|
|
9f12fc5a04 | ||
|
|
813e1ac2dc | ||
|
|
3111d48330 | ||
|
|
b8dcbde7b7 | ||
|
|
c74cc13a3b | ||
|
|
35cc716fcb | ||
|
|
163873d7a8 | ||
|
|
0e18f17dc9 | ||
|
|
d6ea28ee6b | ||
|
|
093c6be85a | ||
|
|
28faa252a2 | ||
|
|
334df22cfd | ||
|
|
b32963854b | ||
|
|
ad153a3fc2 | ||
|
|
caa0fd6904 | ||
|
|
bd7e1193bf | ||
|
|
7c4d95aa2d | ||
|
|
7f8ffbf914 | ||
|
|
d3c543a42e | ||
|
|
4931e43b67 | ||
|
|
4e26150542 | ||
|
|
bf3d8dd26d | ||
|
|
7c58e2bea2 | ||
|
|
ae5b736f40 | ||
|
|
c1b092e0b1 | ||
|
|
adf7582e8b | ||
|
|
544c2d1e41 | ||
|
|
c3f60c5e24 | ||
|
|
b16246455e | ||
|
|
fdc447761c | ||
|
|
7f5563fa97 | ||
|
|
767381b2a0 | ||
|
|
3ad3e7ea16 | ||
|
|
f6fbc71092 | ||
|
|
0943d420bc | ||
|
|
48cac4fb6c | ||
|
|
c56ec8cd79 | ||
|
|
748d2fd11f | ||
|
|
24eb903b56 | ||
|
|
ebbe30ccf9 | ||
|
|
65e5d60984 | ||
|
|
2279f0b33c | ||
|
|
8fa8d8cb2a | ||
|
|
40d3a9e9ce | ||
|
|
6a8dcfae79 | ||
|
|
85976e1c08 | ||
|
|
4bf3c81e4e | ||
|
|
61e94ed50c | ||
|
|
5c964ef5f7 | ||
|
|
8db0023815 | ||
|
|
060a2e9655 | ||
|
|
d7595f46ff | ||
|
|
d2c11089c8 | ||
|
|
d7fd217770 | ||
|
|
00ada50f06 | ||
|
|
5d88af462f | ||
|
|
eff6ded259 | ||
|
|
2f103128f3 | ||
|
|
a48ac18522 | ||
|
|
1f6d37afed | ||
|
|
5f706e0282 | ||
|
|
1a8855547b | ||
|
|
d0062e71e8 | ||
|
|
353a671c27 | ||
|
|
a46177893c | ||
|
|
65c0ddb289 | ||
|
|
78203aaead | ||
|
|
7cba9ee95f | ||
|
|
7e6b01825b | ||
|
|
79d79ca3d6 | ||
|
|
1e415a30aa | ||
|
|
8461c8822a | ||
|
|
eac4363913 | ||
|
|
a3bf84bcea | ||
|
|
8284ac3169 | ||
|
|
68ef36fafb | ||
|
|
1af99634b4 | ||
|
|
53353c6bac | ||
|
|
a73303d6e6 | ||
|
|
18b0458fa5 | ||
|
|
44eeda157a | ||
|
|
062b29960f | ||
|
|
d4e38c6d85 | ||
|
|
25ba610c6a | ||
|
|
6d2ff0933a | ||
|
|
7a6afb4654 | ||
|
|
f5fdd1008f | ||
|
|
0857c654d2 | ||
|
|
480c3ee027 | ||
|
|
54076af43d | ||
|
|
608f255ab0 | ||
|
|
a927b6e01e | ||
|
|
987a7657ce | ||
|
|
94a78b3122 | ||
|
|
fcc458bf31 | ||
|
|
14987c1170 | ||
|
|
46b0ceede3 | ||
|
|
4bbb447e74 | ||
|
|
ebbbbb04ab | ||
|
|
54bad805c1 | ||
|
|
cecade9845 | ||
|
|
f61a08fe50 | ||
|
|
573fbb8fb1 | ||
|
|
e99c07192d | ||
|
|
9252465cab | ||
|
|
a778eb1cc9 | ||
|
|
2bf2052248 | ||
|
|
d903336fe0 | ||
|
|
b7dcff0bbc | ||
|
|
3e6ceb4bb9 | ||
|
|
b2767947a0 | ||
|
|
a24258834e | ||
|
|
76070234d4 | ||
|
|
d2ea17ec30 | ||
|
|
c0e05be791 | ||
|
|
12a2dee175 | ||
|
|
ac3dcdd0d0 | ||
|
|
4257f6a199 | ||
|
|
5fd9fc5e26 | ||
|
|
adbc95c5d5 | ||
|
|
99a1e4aa74 | ||
|
|
2ae82d71d6 | ||
|
|
8a2faf2955 | ||
|
|
1d41f4b138 | ||
|
|
e1c96655b1 | ||
|
|
68fe4a1dc1 | ||
|
|
adf15b7bd3 | ||
|
|
e77ac9617d | ||
|
|
54824879c5 | ||
|
|
bf8580248b | ||
|
|
e1978541db | ||
|
|
aa7580aa5a | ||
|
|
4366cd8c81 | ||
|
|
2a5242a4d9 | ||
|
|
a4dc545a4f | ||
|
|
f16ce5e4a4 | ||
|
|
03cdf67439 | ||
|
|
e8e86dcc92 | ||
|
|
abf969a64d | ||
|
|
34a55135fb | ||
|
|
dd1d534045 | ||
|
|
c39a532da9 | ||
|
|
efb35ff1b0 | ||
|
|
c2b8753c76 | ||
|
|
f8ae8b0be1 | ||
|
|
e8d26aa79e | ||
|
|
a3e3b0d8c6 | ||
|
|
23f31b1639 | ||
|
|
8d164340c6 | ||
|
|
94ad66661e | ||
|
|
f39f90728f | ||
|
|
a7125b7700 | ||
|
|
9f50b0efaa | ||
|
|
0d5ed76979 | ||
|
|
c37f8f1c4c | ||
|
|
f74777e498 | ||
|
|
55c094a58a | ||
|
|
3d6199e0d8 | ||
|
|
8111761c4f | ||
|
|
4fcdb50928 | ||
|
|
23eba2524e | ||
|
|
678a62f152 | ||
|
|
a2940a4ba8 | ||
|
|
70def702b8 | ||
|
|
f72dc51475 | ||
|
|
afe8aba912 | ||
|
|
444cadf864 | ||
|
|
c2d46d1dff | ||
|
|
6808af107b | ||
|
|
720ecae5bb | ||
|
|
490055e74c | ||
|
|
461529dd98 | ||
|
|
8d7085c18f | ||
|
|
b84581c7ee | ||
|
|
b9dd2f5e79 | ||
|
|
12bcf8b2da | ||
|
|
64a66f6590 | ||
|
|
cd69f6287f | ||
|
|
7dee4c82aa | ||
|
|
dc4675b99a | ||
|
|
ba493cbeb3 | ||
|
|
872050a2cd | ||
|
|
4737a8b660 | ||
|
|
9b81aedd9a | ||
|
|
c64c6fe95d | ||
|
|
a53fb7f49a | ||
|
|
d95e44f57c | ||
|
|
4b19af1dfe | ||
|
|
b4949aaf4f | ||
|
|
b9d48aa980 | ||
|
|
59baa97e91 | ||
|
|
394cb56ba0 | ||
|
|
62a2d6ef96 | ||
|
|
dcfebc38bd | ||
|
|
1ffc598064 | ||
|
|
fee0eac14c | ||
|
|
c56e305aec | ||
|
|
1bb39bc9fd | ||
|
|
c8de2df2cf | ||
|
|
7f14cc2751 | ||
|
|
35293e1b46 | ||
|
|
d2bc7b2adc | ||
|
|
f3777a499b | ||
|
|
f491d23d3b | ||
|
|
d4c3fad8c5 | ||
|
|
d9c8fc5c78 | ||
|
|
c1cbb62ee1 | ||
|
|
2fae0e2258 | ||
|
|
c9bac5b544 | ||
|
|
1a7a9236b7 | ||
|
|
6d69355ab9 | ||
|
|
49b4660360 | ||
|
|
ea38c9cd5c | ||
|
|
3ebd64f4da | ||
|
|
5e23d979d2 | ||
|
|
b64f8e8c21 | ||
|
|
2cbad648b9 | ||
|
|
88bcaaecc3 | ||
|
|
176a023a99 | ||
|
|
7f7883a312 | ||
|
|
84ea13eef2 | ||
|
|
4cab3e8d3b | ||
|
|
98ddca52ca | ||
|
|
3ba37b2b2b | ||
|
|
b4a3d7d732 | ||
|
|
3d5317f3da | ||
|
|
5891f170c8 | ||
|
|
f94d1b8af5 | ||
|
|
956b4a8e49 | ||
|
|
ce184408df | ||
|
|
140db42675 | ||
|
|
28b723d6cf | ||
|
|
61c909f551 | ||
|
|
ffda647cdf | ||
|
|
5897d6a644 | ||
|
|
34f7a57c5d | ||
|
|
5b5951ec3c | ||
|
|
b20761cea0 | ||
|
|
5449622d2a | ||
|
|
dd31fb37c3 | ||
|
|
2e453f2257 | ||
|
|
1f4528d597 | ||
|
|
c83aeb17c0 | ||
|
|
5a18280057 | ||
|
|
8e380b94f4 | ||
|
|
ebe8b7a6f0 | ||
|
|
ed4466d934 | ||
|
|
37601187b2 | ||
|
|
366d406f57 | ||
|
|
68ad98fc49 | ||
|
|
65edac267e | ||
|
|
ad0046ab01 | ||
|
|
c3fbc5816e | ||
|
|
11890d887b | ||
|
|
ac50d46c78 | ||
|
|
de298a4aad | ||
|
|
4de32ab8d2 | ||
|
|
8505579b37 | ||
|
|
7c59d647f5 | ||
|
|
8d8e4e4403 | ||
|
|
52b4eeea32 | ||
|
|
28291306cb | ||
|
|
4da7c60aee | ||
|
|
1ae24263ad | ||
|
|
d6248424a1 | ||
|
|
455c67ad3d | ||
|
|
f6ea588349 | ||
|
|
60fe631616 | ||
|
|
221796f746 | ||
|
|
97941bc87c | ||
|
|
b8a7167c4e | ||
|
|
dc00231ec1 | ||
|
|
8a8ea6c6a9 | ||
|
|
9714d0d513 | ||
|
|
6691438fa1 | ||
|
|
426c6c3b9a | ||
|
|
8088cb2902 | ||
|
|
a2ce23aa96 | ||
|
|
95ba16cdb2 | ||
|
|
83fe77b222 | ||
|
|
7ab30723cd | ||
|
|
3f9fc0eaa5 | ||
|
|
ee4351e55d | ||
|
|
ebf55608d7 | ||
|
|
b3fd5f7562 | ||
|
|
3c9ec5f14c | ||
|
|
81c0b70a6f | ||
|
|
a6f307c6b5 | ||
|
|
6f677e2a59 | ||
|
|
612fe3a9ec | ||
|
|
0a9c745d96 | ||
|
|
b64c060ef4 | ||
|
|
8e22763f16 | ||
|
|
40b64e620e | ||
|
|
700cc53c07 | ||
|
|
44d75b169c | ||
|
|
c0bf371b9e | ||
|
|
e362644a55 | ||
|
|
c079c1b60d | ||
|
|
4a3bdf3b53 | ||
|
|
5ff269d2e3 | ||
|
|
3fb2d3fe61 | ||
|
|
6c72c894f7 | ||
|
|
63af43f19d | ||
|
|
64bb5aed8a |
8
.gitignore
vendored
8
.gitignore
vendored
@@ -12,3 +12,11 @@ vendor/github.com/prometheus/client_model/.classpath
|
||||
vendor/github.com/prometheus/client_model/.project
|
||||
vendor/github.com/prometheus/client_model/.settings*
|
||||
gosec_results.json
|
||||
internal/qmgrauth/qmgroam/patch
|
||||
.tagcache
|
||||
|
||||
# Nix
|
||||
*.nix
|
||||
.envrc
|
||||
.direnv
|
||||
result
|
||||
|
||||
108
.travis.yml
108
.travis.yml
@@ -1,4 +1,4 @@
|
||||
# © Copyright IBM Corporation 2018, 2019
|
||||
# © Copyright IBM Corporation 2018, 2020
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
@@ -12,68 +12,90 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
dist: xenial
|
||||
|
||||
dist: bionic
|
||||
group: beta
|
||||
sudo: required
|
||||
language: go
|
||||
|
||||
go:
|
||||
- "1.10"
|
||||
- "1.19.9"
|
||||
|
||||
services:
|
||||
- docker
|
||||
|
||||
env:
|
||||
global:
|
||||
- MAIN_BRANCH=v9.3.0.x
|
||||
- TAGCACHE_FILE=tagcache
|
||||
- RELEASE=r3
|
||||
|
||||
go_import_path: "github.com/ibm-messaging/mq-container"
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- downloads
|
||||
# cache:
|
||||
# directories:
|
||||
# - downloads
|
||||
|
||||
jobs:
|
||||
include:
|
||||
- stage: build and test
|
||||
- stage: basic-build
|
||||
if: branch != v9.3.0.x AND tag IS blank
|
||||
name: "Basic AMD64 build"
|
||||
os: linux
|
||||
env:
|
||||
- BASE_IMAGE=ubuntu:16.04
|
||||
- DOCKER_DOWNGRADE="echo nothing to be done"
|
||||
- env:
|
||||
- BASE_IMAGE=centos:7
|
||||
- DOCKER_DOWNGRADE="echo nothing to be done"
|
||||
- if: type IN (pull_request) OR tag IS present
|
||||
env:
|
||||
- BASE_IMAGE=ubuntu:16.04
|
||||
- DOCKER_DOWNGRADE="docker save -o images.tar mqadvanced-server-dev mq-dev-jms-test &&
|
||||
sudo apt-get autoremove -y docker-ce &&
|
||||
curl -fsSL \"https://apt.dockerproject.org/gpg\" | sudo apt-key add - &&
|
||||
sudo apt-add-repository \"deb https://apt.dockerproject.org/repo ubuntu-$(lsb_release -cs) main\" &&
|
||||
sudo apt-get update &&
|
||||
sudo apt-get install docker-engine=1.12.6-0~ubuntu-$(lsb_release -cs) &&
|
||||
docker load -q -i images.tar &&
|
||||
export DOCKER_API_VERSION=\"1.24\""
|
||||
- MQ_ARCHIVE_REPOSITORY_DEV=$MQ_930_ARCHIVE_REPOSITORY_DEV_AMD64
|
||||
script: bash -e travis-build-scripts/run.sh
|
||||
|
||||
# CD Build
|
||||
|
||||
- stage: global-tag
|
||||
if: branch = v9.3.0.x AND type != pull_request OR tag =~ ^release-candidate*
|
||||
name: "Generate Global Tag"
|
||||
os: linux
|
||||
script: bash -e travis-build-scripts/global-tag.sh
|
||||
- stage: build
|
||||
if: branch = v9.3.0.x OR tag =~ ^release-candidate*
|
||||
name: "Multi-Arch AMD64 build"
|
||||
os: linux
|
||||
env:
|
||||
- BUILD_ALL=true
|
||||
- MQ_ARCHIVE_REPOSITORY=$MQ_930_ARCHIVE_REPOSITORY_AMD64
|
||||
- MQ_ARCHIVE_REPOSITORY_DEV=$MQ_930_ARCHIVE_REPOSITORY_DEV_AMD64
|
||||
script: bash -e travis-build-scripts/run.sh
|
||||
- stage: build
|
||||
if: branch = v9.3.0.x OR tag =~ ^release-candidate*
|
||||
name: "Multi-Arch S390X build"
|
||||
os: linux-s390
|
||||
env:
|
||||
- BUILD_ALL=true
|
||||
- TEST_OPTS_DOCKER="-run TestGoldenPathWithMetrics"
|
||||
- MQ_ARCHIVE_REPOSITORY=$MQ_930_ARCHIVE_REPOSITORY_S390X
|
||||
- MQ_ARCHIVE_REPOSITORY_DEV=$MQ_930_ARCHIVE_REPOSITORY_DEV_S390X
|
||||
script: bash -e travis-build-scripts/run.sh
|
||||
- stage: build
|
||||
if: branch = v9.3.0.x OR tag =~ ^release-candidate*
|
||||
name: "Multi-Arch PPC64LE build"
|
||||
os: linux-ppc64le
|
||||
env:
|
||||
- BUILD_ALL=true
|
||||
- TEST_OPTS_DOCKER="-run TestGoldenPathWithMetrics"
|
||||
- MQ_ARCHIVE_REPOSITORY=$MQ_930_ARCHIVE_REPOSITORY_PPC64LE
|
||||
- MQ_ARCHIVE_REPOSITORY_DEV=$MQ_930_ARCHIVE_REPOSITORY_DEV_PPC64LE
|
||||
script: bash -e travis-build-scripts/run.sh
|
||||
- stage: push-manifest
|
||||
if: branch = v9.3.0.x AND type != pull_request OR tag =~ ^release-candidate*
|
||||
name: "Push Manifest-list to registry"
|
||||
env:
|
||||
- PUSH_MANIFEST_ONLY=true
|
||||
script: bash -e travis-build-scripts/run.sh
|
||||
|
||||
before_install:
|
||||
- ./install-build-deps-ubuntu.sh
|
||||
- make install-build-deps
|
||||
- make install-credential-helper
|
||||
|
||||
install:
|
||||
- echo nothing
|
||||
|
||||
before_script:
|
||||
- echo 'Downloading Go dependencies...' && echo -en 'travis_fold:start:deps\\r'
|
||||
- make deps
|
||||
- echo -en 'travis_fold:end:deps\\r'
|
||||
- echo 'Building Developer image...' && echo -en 'travis_fold:start:build-devserver\\r'
|
||||
- make build-devserver
|
||||
- echo -en 'travis_fold:end:build-devserver\\r'
|
||||
- echo 'Building Developer JMS test image...' && echo -en 'travis_fold:start:build-devjmstest\\r'
|
||||
- make build-devjmstest
|
||||
- echo -en 'travis_fold:end:build-devjmstest\\r'
|
||||
|
||||
script:
|
||||
- echo 'Downgrading Docker (if necessary)...' && echo -en 'travis_fold:start:docker-downgrade\\r'
|
||||
- eval "$DOCKER_DOWNGRADE"
|
||||
- echo -en 'travis_fold:end:docker-downgrade\\r'
|
||||
- echo 'Testing Developer image...' && echo -en 'travis_fold:start:test-devserver\\r'
|
||||
- make test-devserver
|
||||
- echo -en 'travis_fold:end:test-devserver\\r'
|
||||
before_script: echo nothing
|
||||
|
||||
after_success:
|
||||
- make lint
|
||||
|
||||
6
.whitesource
Normal file
6
.whitesource
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"settingsInheritedFrom": "whitesource-config/whitesource-config@master",
|
||||
"scanSettings": {
|
||||
"baseBranches": ["private-master", "v9.2.0.x-eus", "v9.2.5"]
|
||||
}
|
||||
}
|
||||
89
CHANGELOG.md
89
CHANGELOG.md
@@ -1,7 +1,96 @@
|
||||
# Change log
|
||||
|
||||
## 9.3.0.5-LTS (2023-04)
|
||||
|
||||
* Updated to MQ version 9.3.0.5
|
||||
|
||||
## 9.3.0.4-LTS (2023-02)
|
||||
|
||||
* Updated to MQ version 9.3.0.4
|
||||
|
||||
## 9.3.0.3-LTS (2023-01)
|
||||
|
||||
* Updated to MQ version 9.3.0.3
|
||||
|
||||
## 9.3.0.1-LTS (2022-09)
|
||||
|
||||
* Updated to MQ version 9.3.0.1
|
||||
|
||||
## 9.3.0.0 (2022-06)
|
||||
|
||||
* Updated to MQ version 9.3.0.0
|
||||
* Use `registry.access.redhat.com` instead of `registry.redhat.io`, so that you don't need to login with a Red Hat account.
|
||||
* Updated default developer config to use TLS cipher `ANY_TLS12_OR_HIGHER` instead of `ANY_TLS12`
|
||||
* Added default `jvm.options` file fix issue with missing preferences file causing an error in the web server log.
|
||||
* Updated to allow building image from Podman on macOS (requires Podman 4.1)
|
||||
* Container builds are now faster
|
||||
* Updated signal handling to use a buffer, as recommended by the Go 1.17 vetting tool
|
||||
|
||||
## 9.2.5.0 (2022-03)
|
||||
|
||||
* Updated to MQ version 9.2.5.0
|
||||
|
||||
## 9.2.4.0 (2021-11)
|
||||
|
||||
* Updated to MQ version 9.2.4.0
|
||||
|
||||
## 9.2.3.0 (2021-07-22)
|
||||
|
||||
* Updated to MQ version 9.2.3.0
|
||||
|
||||
## 9.2.2.0 (2021-03-26)
|
||||
|
||||
* Updated to MQ version 9.2.2.0
|
||||
|
||||
## 9.2.1.0 (2020-02-18)
|
||||
|
||||
* Updated to MQ version 9.2.1.0
|
||||
|
||||
|
||||
## 9.2.0.1-LTS (2020-12-04)
|
||||
|
||||
* Added support for MQ Long Term Support (production licensed only) in the mq-container
|
||||
|
||||
## 9.2.0.0 (2020-07-23)
|
||||
|
||||
* Updated to [MQ version 9.2.0.0](https://www.ibm.com/support/knowledgecenter/SSFKSJ_9.2.0/com.ibm.mq.pro.doc/q113110_.htm)
|
||||
* Use `-ic` arguments with `crtmqm` to process MQSC files in `/etc/mqm`. Replaces previous use of "runmqsc" commands
|
||||
|
||||
## 9.1.5.0 (2020-04-02)
|
||||
|
||||
* Updated to MQ version 9.1.5.0
|
||||
* Can now run as a random user, instead of the "mqm" user, which has now been removed. This adds compatability for the [Red Hat OpenShift restricted SCC](https://docs.openshift.com/container-platform/4.3/authentication/managing-security-context-constraints.html#security-context-constraints-about_configuring-internal-oauth). The default image UID is `1001`.
|
||||
|
||||
## 9.1.4.0 (2019-12-06)
|
||||
|
||||
* Updated to MQ version 9.1.4.0
|
||||
* Updated to use UBI8 as base image
|
||||
* Added required security settings to self signed certificates to align with macOS Catalina requirements
|
||||
|
||||
## 9.1.3.0 (2019-07-19)
|
||||
|
||||
* Updated to MQ version 9.1.3.0
|
||||
* Allow generation of TLS certificate with given hostname
|
||||
* Fixes for the following issues:
|
||||
* `MQ_EPHEMERAL_PREFIX` UNIX sockets fix
|
||||
* Fix Makefile for Windows
|
||||
* Use -a option on crtmqdir
|
||||
* Remove check for certificate environment variable
|
||||
|
||||
## 9.1.2.0-UBI (2019-06-21)
|
||||
|
||||
**Breaking changes**:
|
||||
* UID of the mqm user is now 888. You need to run the container with an entrypoint of `runmqserver -i` under the root user to update any existing files.
|
||||
* MQSC files supplied will be verified before being run. Files containing invalid MQSC will cause the container to fail to start
|
||||
|
||||
**Other changes**:
|
||||
* Security fixes
|
||||
* Web console added to production image
|
||||
* Container built on RedHat host
|
||||
|
||||
## 9.1.2.0 (2019-03-21)
|
||||
|
||||
* Updated to MQ version 9.1.2.0
|
||||
* Now runs using the "mqm" user instead of root. See new [security doc](https://github.com/ibm-messaging/mq-container/blob/master/docs/security.md)
|
||||
* New [IGNSTATE](https://www.ibm.com/support/knowledgecenter/en/SSFKSJ_9.1.0/com.ibm.mq.pro.doc/q132310_.htm#q132310___ignstateparm) parameter used in default developer config
|
||||
* Termination log moved from `/dev/termination-log` to `/run/termination-log`, to make permissions easier to handle
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# © Copyright IBM Corporation 2015, 2019
|
||||
# © Copyright IBM Corporation 2015, 2022
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
@@ -12,73 +12,166 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
ARG BASE_IMAGE=ubuntu:16.04
|
||||
ARG BUILDER_IMAGE=mq-golang-sdk:9.1.2.0-x86_64-ubuntu-16.04
|
||||
|
||||
ARG BASE_IMAGE=registry.access.redhat.com/ubi8/ubi-minimal
|
||||
ARG BASE_TAG=8.8-860
|
||||
ARG BUILDER_IMAGE=registry.access.redhat.com/ubi8/go-toolset
|
||||
ARG BUILDER_TAG=1.19.9-2
|
||||
ARG GO_WORKDIR=/opt/app-root/src/go/src/github.com/ibm-messaging/mq-container
|
||||
ARG MQ_URL="https://public.dhe.ibm.com/ibmdl/export/pub/software/websphere/messaging/mqadv/9.3.0.5-IBM-MQ-Advanced-for-Developers-Non-Install-LinuxX64.tar.gz"
|
||||
###############################################################################
|
||||
# Build stage to build Go code
|
||||
###############################################################################
|
||||
FROM $BUILDER_IMAGE as builder
|
||||
WORKDIR /go/src/github.com/ibm-messaging/mq-container/
|
||||
FROM $BUILDER_IMAGE:$BUILDER_TAG as builder
|
||||
# The URL to download the MQ installer from in tar.gz format
|
||||
# This assumes an archive containing the MQ Non-Install packages
|
||||
ARG MQ_URL
|
||||
ARG IMAGE_REVISION="Not specified"
|
||||
ARG IMAGE_SOURCE="Not specified"
|
||||
ARG IMAGE_TAG="Not specified"
|
||||
ARG GO_WORKDIR
|
||||
USER 0
|
||||
WORKDIR /opt/mqm
|
||||
# Download and extract MQ files, to get the MQ client needed to compile.
|
||||
# Only extract certain MQ files to make the build quicker
|
||||
RUN curl --fail --location $MQ_URL | tar --extract --gunzip \
|
||||
&& chown -R 1001:root /opt/mqm/*
|
||||
WORKDIR $GO_WORKDIR/
|
||||
COPY go.mod go.sum ./
|
||||
COPY cmd/ ./cmd
|
||||
COPY internal/ ./internal
|
||||
COPY pkg/ ./pkg
|
||||
COPY vendor/ ./vendor
|
||||
RUN go build -ldflags "-X \"main.ImageCreated=$(date --iso-8601=seconds)\" -X \"main.ImageRevision=$IMAGE_REVISION\" -X \"main.ImageSource=$IMAGE_SOURCE\" -X \"main.ImageTag=$IMAGE_TAG\"" ./cmd/runmqserver/
|
||||
RUN go build ./cmd/chkmqready/
|
||||
RUN go build ./cmd/chkmqhealthy/
|
||||
# Run all unit tests
|
||||
RUN go test -v ./cmd/runmqserver/
|
||||
RUN go test -v ./cmd/chkmqready/
|
||||
RUN go test -v ./cmd/chkmqhealthy/
|
||||
RUN go test -v ./internal/...
|
||||
RUN go vet ./cmd/... ./internal/...
|
||||
ENV CGO_CFLAGS="-I/opt/mqm/inc/" \
|
||||
CGO_LDFLAGS_ALLOW="-Wl,-rpath.*" \
|
||||
PATH="${PATH}:/opt/mqm/bin"
|
||||
RUN go build -ldflags "-X \"main.ImageCreated=$(date --iso-8601=seconds)\" -X \"main.ImageRevision=$IMAGE_REVISION\" -X \"main.ImageSource=$IMAGE_SOURCE\" -X \"main.ImageTag=$IMAGE_TAG\"" ./cmd/runmqserver/ \
|
||||
&& go build ./cmd/chkmqready/ \
|
||||
&& go build ./cmd/chkmqhealthy/ \
|
||||
&& go build ./cmd/chkmqstarted/ \
|
||||
&& go build ./cmd/runmqdevserver/ \
|
||||
&& go test -v ./cmd/runmqdevserver/... \
|
||||
&& go test -v ./cmd/runmqserver/ \
|
||||
&& go test -v ./cmd/chkmqready/ \
|
||||
&& go test -v ./cmd/chkmqhealthy/ \
|
||||
&& go test -v ./cmd/chkmqstarted/ \
|
||||
&& go test -v ./pkg/... \
|
||||
&& go test -v ./internal/... \
|
||||
&& go vet ./cmd/... ./internal/...
|
||||
|
||||
###############################################################################
|
||||
# Main build stage, to build MQ image
|
||||
###############################################################################
|
||||
FROM $BASE_IMAGE
|
||||
|
||||
# The URL to download the MQ installer from in tar.gz format
|
||||
# This assumes an archive containing the MQ Debian (.deb) install packages
|
||||
ARG MQ_URL
|
||||
|
||||
FROM $BASE_IMAGE:$BASE_TAG AS mq-server
|
||||
# The MQ packages to install - see install-mq.sh for default value
|
||||
ARG MQ_PACKAGES
|
||||
|
||||
# The UID to use for the "mqm" user
|
||||
ARG MQM_UID=999
|
||||
|
||||
ARG MQ_URL
|
||||
ARG BASE_IMAGE
|
||||
ARG BASE_TAG
|
||||
ARG GO_WORKDIR
|
||||
LABEL summary="IBM MQ Advanced Server" \
|
||||
description="Simplify, accelerate and facilitate the reliable exchange of data with a security-rich messaging solution — trusted by the world’s most successful enterprises" \
|
||||
vendor="IBM" \
|
||||
maintainer="IBM" \
|
||||
distribution-scope="private" \
|
||||
authoritative-source-url="https://www.ibm.com/software/passportadvantage/" \
|
||||
url="https://www.ibm.com/products/mq/advanced" \
|
||||
io.openshift.tags="mq messaging" \
|
||||
io.k8s.display-name="IBM MQ Advanced Server" \
|
||||
io.k8s.description="Simplify, accelerate and facilitate the reliable exchange of data with a security-rich messaging solution — trusted by the world’s most successful enterprises" \
|
||||
base-image=$BASE_IMAGE \
|
||||
base-image-release=$BASE_TAG
|
||||
COPY install-mq.sh /usr/local/bin/
|
||||
|
||||
COPY install-mq-server-prereqs.sh /usr/local/bin/
|
||||
# Install MQ. To avoid a "text file busy" error here, we sleep before installing.
|
||||
RUN chmod u+x /usr/local/bin/install-mq.sh \
|
||||
RUN env \
|
||||
&& mkdir /opt/mqm \
|
||||
&& chmod u+x /usr/local/bin/install-*.sh \
|
||||
&& sleep 1 \
|
||||
&& install-mq.sh $MQM_UID
|
||||
|
||||
# Create a directory for runtime data from runmqserver
|
||||
RUN mkdir -p /run/runmqserver \
|
||||
&& chown mqm:mqm /run/runmqserver
|
||||
|
||||
COPY --from=builder /go/src/github.com/ibm-messaging/mq-container/runmqserver /usr/local/bin/
|
||||
COPY --from=builder /go/src/github.com/ibm-messaging/mq-container/chkmq* /usr/local/bin/
|
||||
&& install-mq-server-prereqs.sh \
|
||||
&& install-mq.sh \
|
||||
&& /opt/mqm/bin/security/amqpamcf \
|
||||
&& chown -R 1001:root /opt/mqm/*
|
||||
COPY --from=builder $GO_WORKDIR/runmqserver /usr/local/bin/
|
||||
COPY --from=builder $GO_WORKDIR/chkmq* /usr/local/bin/
|
||||
COPY NOTICES.txt /opt/mqm/licenses/notices-container.txt
|
||||
|
||||
COPY ha/native-ha.ini.tpl /etc/mqm/native-ha.ini.tpl
|
||||
# Copy web XML files
|
||||
COPY web /etc/mqm/web
|
||||
COPY etc/mqm/*.tpl /etc/mqm/
|
||||
RUN chmod ug+x /usr/local/bin/runmqserver \
|
||||
&& chown mqm:mqm /usr/local/bin/*mq* \
|
||||
&& chmod ug+xs /usr/local/bin/chkmq* \
|
||||
&& install --directory --mode 0775 --owner mqm --group root /run/runmqserver \
|
||||
&& chown 1001:root /usr/local/bin/*mq* \
|
||||
&& chmod ug+x /usr/local/bin/chkmq* \
|
||||
&& chown -R 1001:root /etc/mqm/* \
|
||||
&& install --directory --mode 2775 --owner 1001 --group root /run/runmqserver \
|
||||
&& touch /run/termination-log \
|
||||
&& chown mqm:root /run/termination-log \
|
||||
&& chmod 0660 /run/termination-log
|
||||
|
||||
&& chown 1001:root /run/termination-log \
|
||||
&& chmod 0660 /run/termination-log \
|
||||
&& chmod -R g+w /etc/mqm/web
|
||||
# Always use port 1414 for MQ & 9157 for the metrics
|
||||
EXPOSE 1414 9157
|
||||
|
||||
EXPOSE 1414 9157 9443
|
||||
ENV MQ_OVERRIDE_DATA_PATH=/mnt/mqm/data MQ_OVERRIDE_INSTALLATION_NAME=Installation1 MQ_USER_NAME="mqm" PATH="${PATH}:/opt/mqm/bin"
|
||||
ENV MQ_GRACE_PERIOD=30
|
||||
ENV LANG=en_US.UTF-8 AMQ_DIAGNOSTIC_MSG_SEVERITY=1 AMQ_ADDITIONAL_JSON_LOG=1 LOG_FORMAT=basic
|
||||
|
||||
USER $MQM_UID
|
||||
|
||||
# We can run as any UID
|
||||
USER 1001
|
||||
ENV MQ_CONNAUTH_USE_HTP=false
|
||||
ENTRYPOINT ["runmqserver"]
|
||||
|
||||
###############################################################################
|
||||
# Build stage to build C code for custom authorization service (developer-only)
|
||||
###############################################################################
|
||||
# Use the Go toolset image, which already includes gcc and the MQ SDK
|
||||
FROM builder as cbuilder
|
||||
# The URL to download the MQ installer from in tar.gz format
|
||||
# This assumes an archive containing the MQ Non-Install packages
|
||||
ARG MQ_URL
|
||||
USER 0
|
||||
# Install the Apache Portable Runtime code (used for htpasswd hash checking)
|
||||
RUN yum --assumeyes --disableplugin=subscription-manager install apr-devel apr-util-openssl apr-util-devel
|
||||
COPY authservice/ /opt/app-root/src/authservice/
|
||||
WORKDIR /opt/app-root/src/authservice/mqhtpass
|
||||
RUN make all
|
||||
|
||||
###############################################################################
|
||||
# Add default developer config
|
||||
###############################################################################
|
||||
FROM mq-server AS mq-dev-server
|
||||
ARG BASE_IMAGE
|
||||
ARG BASE_TAG
|
||||
ARG GO_WORKDIR
|
||||
LABEL summary="IBM MQ Advanced for Developers Server" \
|
||||
description="Simplify, accelerate and facilitate the reliable exchange of data with a security-rich messaging solution — trusted by the world’s most successful enterprises" \
|
||||
vendor="IBM" \
|
||||
distribution-scope="private" \
|
||||
authoritative-source-url="https://www.ibm.com/software/passportadvantage/" \
|
||||
url="https://www.ibm.com/products/mq/advanced" \
|
||||
io.openshift.tags="mq messaging" \
|
||||
io.k8s.display-name="IBM MQ Advanced for Developers Server" \
|
||||
io.k8s.description="Simplify, accelerate and facilitate the reliable exchange of data with a security-rich messaging solution — trusted by the world’s most successful enterprises" \
|
||||
base-image=$BASE_IMAGE \
|
||||
base-image-release=$BASE_TAG
|
||||
USER 0
|
||||
COPY --from=cbuilder /opt/app-root/src/authservice/mqhtpass/build/mqhtpass.so /opt/mqm/lib64/
|
||||
COPY etc/mqm/*.ini /etc/mqm/
|
||||
COPY etc/mqm/mq.htpasswd /etc/mqm/
|
||||
COPY incubating/mqadvanced-server-dev/install-extra-packages.sh /usr/local/bin/
|
||||
RUN chmod u+x /usr/local/bin/install-extra-packages.sh \
|
||||
&& sleep 1 \
|
||||
&& install-extra-packages.sh
|
||||
COPY --from=builder $GO_WORKDIR/runmqdevserver /usr/local/bin/
|
||||
# Copy template files
|
||||
COPY incubating/mqadvanced-server-dev/*.tpl /etc/mqm/
|
||||
# Copy web XML files for default developer configuration
|
||||
COPY incubating/mqadvanced-server-dev/web /etc/mqm/web
|
||||
RUN chown -R 1001:root /etc/mqm/* \
|
||||
&& chmod -R g+w /etc/mqm/web \
|
||||
&& chmod +x /usr/local/bin/runmq* \
|
||||
&& chmod 0660 /etc/mqm/mq.htpasswd \
|
||||
&& install --directory --mode 2775 --owner 1001 --group root /run/runmqdevserver
|
||||
ENV MQ_DEV=true \
|
||||
MQ_ENABLE_EMBEDDED_WEB_SERVER=1 \
|
||||
MQ_GENERATE_CERTIFICATE_HOSTNAME=localhost \
|
||||
LD_LIBRARY_PATH=/opt/mqm/lib64 \
|
||||
MQ_CONNAUTH_USE_HTP=true \
|
||||
MQS_PERMIT_UNKNOWN_ID=true
|
||||
USER 1001
|
||||
ENTRYPOINT ["runmqdevserver"]
|
||||
|
||||
625
Makefile
625
Makefile
@@ -1,4 +1,4 @@
|
||||
# © Copyright IBM Corporation 2018
|
||||
# © Copyright IBM Corporation 2017, 2023
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
@@ -13,115 +13,538 @@
|
||||
# limitations under the License.
|
||||
|
||||
###############################################################################
|
||||
# Variables
|
||||
# Conditional variables - you can override the values of these variables from
|
||||
# the command line
|
||||
###############################################################################
|
||||
GO_PKG_DIRS = ./cmd ./internal ./test
|
||||
|
||||
# Set variable if running on a Red Hat Enterprise Linux host
|
||||
ifneq ($(wildcard /etc/redhat-release),)
|
||||
REDHAT_RELEASE = $(shell cat /etc/redhat-release)
|
||||
ifeq "$(findstring Red Hat,$(REDHAT_RELEASE))" "Red Hat"
|
||||
RHEL_HOST = "true"
|
||||
include config.env
|
||||
include source-branch.env
|
||||
|
||||
# RELEASE shows what release of the container code has been built
|
||||
RELEASE ?=
|
||||
# MQ_ARCHIVE_REPOSITORY is a remote repository from which to pull the MQ_ARCHIVE (if required)
|
||||
MQ_ARCHIVE_REPOSITORY ?=
|
||||
# MQ_ARCHIVE_REPOSITORY_DEV is a remote repository from which to pull the MQ_ARCHIVE_DEV (if required)
|
||||
MQ_ARCHIVE_REPOSITORY_DEV ?=
|
||||
# MQ_ARCHIVE_REPOSITORY_USER is the user for the remote repository (if required)
|
||||
MQ_ARCHIVE_REPOSITORY_USER ?=
|
||||
# MQ_ARCHIVE_REPOSITORY_CREDENTIAL is the password/API key for the remote repository (if required)
|
||||
MQ_ARCHIVE_REPOSITORY_CREDENTIAL ?=
|
||||
# MQ_ARCHIVE is the name of the file, under the downloads directory, from which MQ Advanced can
|
||||
# be installed. Does not apply to MQ Advanced for Developers
|
||||
MQ_ARCHIVE ?= IBM_MQ_$(MQ_VERSION_VRM)_$(MQ_ARCHIVE_TYPE)_$(MQ_ARCHIVE_ARCH)_NOINST.tar.gz
|
||||
# MQ_ARCHIVE_DEV is the name of the file, under the downloads directory, from which MQ Advanced
|
||||
# for Developers can be installed
|
||||
MQ_ARCHIVE_DEV ?= $(MQ_VERSION)-IBM-MQ-Advanced-for-Developers-Non-Install-$(MQ_ARCHIVE_DEV_TYPE)$(MQ_ARCHIVE_DEV_ARCH).tar.gz
|
||||
# MQ_SDK_ARCHIVE specifies the archive to use for building the golang programs. Defaults vary on developer or advanced.
|
||||
MQ_SDK_ARCHIVE ?= $(MQ_ARCHIVE_DEV_$(MQ_VERSION))
|
||||
# Options to `go test` for the Docker tests
|
||||
TEST_OPTS_DOCKER ?=
|
||||
# Timeout for the Docker tests
|
||||
TEST_TIMEOUT_DOCKER ?= 30m
|
||||
# MQ_IMAGE_ADVANCEDSERVER is the name of the built MQ Advanced image
|
||||
MQ_IMAGE_ADVANCEDSERVER ?=ibm-mqadvanced-server
|
||||
# MQ_IMAGE_DEVSERVER is the name of the built MQ Advanced for Developers image
|
||||
MQ_IMAGE_DEVSERVER ?=ibm-mqadvanced-server-dev
|
||||
# MQ_MANIFEST_TAG is the tag to use for fat-manifest
|
||||
MQ_MANIFEST_TAG ?= $(MQ_VERSION)$(RELEASE_TAG)$(LTS_TAG)$(MQ_MANIFEST_TAG_SUFFIX)
|
||||
# MQ_TAG is the tag of the built MQ Advanced image & MQ Advanced for Developers image
|
||||
MQ_TAG ?= $(MQ_MANIFEST_TAG)-$(ARCH)
|
||||
# COMMAND is the container command to run. "podman" or "docker"
|
||||
COMMAND ?=$(shell type -p podman 2>&1 >/dev/null && echo podman || echo docker)
|
||||
# MQ_DELIVERY_REGISTRY_HOSTNAME is a remote registry to push the MQ Image to (if required)
|
||||
MQ_DELIVERY_REGISTRY_HOSTNAME ?=
|
||||
# MQ_DELIVERY_REGISTRY_NAMESPACE is the namespace/path on the delivery registry (if required)
|
||||
MQ_DELIVERY_REGISTRY_NAMESPACE ?=
|
||||
# MQ_DELIVERY_REGISTRY_USER is the user for the remote registry (if required)
|
||||
MQ_DELIVERY_REGISTRY_USER ?=
|
||||
# MQ_DELIVERY_REGISTRY_CREDENTIAL is the password/API key for the remote registry (if required)
|
||||
MQ_DELIVERY_REGISTRY_CREDENTIAL ?=
|
||||
# ARCH is the platform architecture (e.g. amd64, ppc64le or s390x)
|
||||
ARCH ?= $(if $(findstring x86_64,$(shell uname -m)),amd64,$(shell uname -m))
|
||||
# LTS is a boolean value to enable/disable LTS container build
|
||||
LTS ?= false
|
||||
# VOLUME_MOUNT_OPTIONS is used when bind-mounting files from the "downloads" directory into the container. By default, SELinux labels are automatically re-written, but this doesn't work on some filesystems with extended attributes (xattrs). You can turn off the label re-writing by setting this variable to be blank.
|
||||
VOLUME_MOUNT_OPTIONS ?= :Z
|
||||
|
||||
###############################################################################
|
||||
# Other variables
|
||||
###############################################################################
|
||||
# Lock Docker API version for compatibility with Podman and with the Docker version in Travis' Ubuntu Bionic
|
||||
DOCKER_API_VERSION=1.40
|
||||
GO_PKG_DIRS = ./cmd ./internal ./test
|
||||
MQ_ARCHIVE_TYPE=LINUX
|
||||
MQ_ARCHIVE_DEV_TYPE=Linux
|
||||
# BUILD_SERVER_CONTAINER is the name of the web server container used at build time
|
||||
BUILD_SERVER_CONTAINER=build-server
|
||||
# BUILD_SERVER_NETWORK is the name of the network to use for the web server container used at build time
|
||||
BUILD_SERVER_NETWORK=build
|
||||
# NUM_CPU is the number of CPUs available to Docker. Used to control how many
|
||||
# test run in parallel
|
||||
NUM_CPU ?= $(or $(shell $(COMMAND) info --format "{{ .NCPU }}"),2)
|
||||
# BASE_IMAGE_TAG is a normalized version of BASE_IMAGE, suitable for use in a Docker tag
|
||||
BASE_IMAGE_TAG=$(lastword $(subst /, ,$(subst :,-,$(BASE_IMAGE))))
|
||||
#BASE_IMAGE_TAG=$(subst /,-,$(subst :,-,$(BASE_IMAGE)))
|
||||
MQ_IMAGE_DEVSERVER_BASE=mqadvanced-server-dev-base
|
||||
# Docker image name to use for JMS tests
|
||||
DEV_JMS_IMAGE=mq-dev-jms-test
|
||||
# Variables for versioning
|
||||
IMAGE_REVISION=$(shell git rev-parse HEAD)
|
||||
IMAGE_SOURCE=$(shell git config --get remote.origin.url)
|
||||
EMPTY:=
|
||||
SPACE:= $(EMPTY) $(EMPTY)
|
||||
# MQ_VERSION_VRM is MQ_VERSION with only the Version, Release and Modifier fields (no Fix field). e.g. 9.2.0 instead of 9.2.0.0
|
||||
MQ_VERSION_VRM=$(subst $(SPACE),.,$(wordlist 1,3,$(subst .,$(SPACE),$(MQ_VERSION))))
|
||||
|
||||
ifneq (,$(findstring Microsoft,$(shell uname -r)))
|
||||
DOWNLOADS_DIR=$(patsubst /mnt/c%,C:%,$(realpath ./downloads/))
|
||||
else ifneq (,$(findstring Windows,$(shell echo ${OS})))
|
||||
DOWNLOADS_DIR=$(shell pwd)/downloads/
|
||||
else
|
||||
DOWNLOADS_DIR=$(realpath ./downloads/)
|
||||
endif
|
||||
|
||||
# Try to figure out which archive to use from the architecture
|
||||
ifeq "$(ARCH)" "amd64"
|
||||
MQ_ARCHIVE_ARCH=X86-64
|
||||
MQ_ARCHIVE_DEV_ARCH=X64
|
||||
else ifeq "$(ARCH)" "ppc64le"
|
||||
MQ_ARCHIVE_ARCH=PPC64LE
|
||||
else ifeq "$(ARCH)" "s390x"
|
||||
MQ_ARCHIVE_ARCH=S390X
|
||||
endif
|
||||
|
||||
# If this is a fake master build, push images to alternative location (pipeline wont consider these images GA candidates)
|
||||
ifeq ($(shell [ "$(TRAVIS)" = "true" ] && [ -n "$(MAIN_BRANCH)" ] && [ -n "$(SOURCE_BRANCH)" ] && [ "$(MAIN_BRANCH)" != "$(SOURCE_BRANCH)" ] && echo "true"), true)
|
||||
MQ_DELIVERY_REGISTRY_NAMESPACE="master-fake"
|
||||
endif
|
||||
|
||||
# LTS_TAG is the tag modifier for an LTS container build
|
||||
LTS_TAG=
|
||||
ifeq "$(LTS)" "true"
|
||||
ifneq "$(LTS_TAG_OVERRIDE)" "$(EMPTY)"
|
||||
LTS_TAG=$(LTS_TAG_OVERRIDE)
|
||||
else
|
||||
LTS_TAG=-lts
|
||||
endif
|
||||
MQ_ARCHIVE:=$(MQ_VERSION)-IBM-MQ-Advanced-Non-Install-Linux$(MQ_ARCHIVE_ARCH).tar.gz
|
||||
MQ_DELIVERY_REGISTRY_NAMESPACE:=$(MQ_DELIVERY_REGISTRY_NAMESPACE)$(LTS_TAG)
|
||||
endif
|
||||
|
||||
ifneq (,$(findstring release-candidate,$(TRAVIS_TAG)))
|
||||
MQ_DELIVERY_REGISTRY_NAMESPACE=release-candidates
|
||||
endif
|
||||
|
||||
ifneq "$(MQ_DELIVERY_REGISTRY_NAMESPACE)" "$(EMPTY)"
|
||||
MQ_DELIVERY_REGISTRY_FULL_PATH=$(MQ_DELIVERY_REGISTRY_HOSTNAME)/$(MQ_DELIVERY_REGISTRY_NAMESPACE)
|
||||
else
|
||||
MQ_DELIVERY_REGISTRY_FULL_PATH=$(MQ_DELIVERY_REGISTRY_HOSTNAME)
|
||||
endif
|
||||
|
||||
# image tagging
|
||||
|
||||
ifneq "$(RELEASE)" "$(EMPTY)"
|
||||
EXTRA_LABELS_RELEASE=--label "release=$(RELEASE)"
|
||||
RELEASE_TAG="-$(RELEASE)"
|
||||
endif
|
||||
|
||||
ifneq "$(MQ_ARCHIVE_LEVEL)" "$(EMPTY)"
|
||||
EXTRA_LABELS_LEVEL=--label "mq-build=$(MQ_ARCHIVE_LEVEL)"
|
||||
endif
|
||||
|
||||
EXTRA_LABELS=$(EXTRA_LABELS_RELEASE) $(EXTRA_LABELS_LEVEL)
|
||||
|
||||
ifeq "$(TIMESTAMPFLAT)" "$(EMPTY)"
|
||||
TIMESTAMPFLAT=$(shell date "+%Y%m%d%H%M%S")
|
||||
endif
|
||||
|
||||
ifeq "$(GIT_COMMIT)" "$(EMPTY)"
|
||||
GIT_COMMIT=$(shell git rev-parse --short HEAD)
|
||||
endif
|
||||
|
||||
ifeq ($(shell [ ! -z $(TRAVIS) ] && [ "$(TRAVIS_PULL_REQUEST)" = "false" ] && [ "$(TRAVIS_BRANCH)" = "$(MAIN_BRANCH)" ] && echo true), true)
|
||||
MQ_MANIFEST_TAG_SUFFIX=.$(TIMESTAMPFLAT).$(GIT_COMMIT)
|
||||
endif
|
||||
|
||||
PATH_TO_MQ_TAG_CACHE=$(TRAVIS_BUILD_DIR)/.tagcache
|
||||
ifneq "$(TRAVIS)" "$(EMPTY)"
|
||||
ifneq ("$(wildcard $(PATH_TO_MQ_TAG_CACHE))","")
|
||||
include $(PATH_TO_MQ_TAG_CACHE)
|
||||
endif
|
||||
endif
|
||||
|
||||
MQ_AMD64_TAG=$(MQ_MANIFEST_TAG)-amd64
|
||||
MQ_S390X_TAG?=$(MQ_MANIFEST_TAG)-s390x
|
||||
MQ_PPC64LE_TAG?=$(MQ_MANIFEST_TAG)-ppc64le
|
||||
|
||||
# end image tagging
|
||||
|
||||
MQ_IMAGE_FULL_RELEASE_NAME=$(MQ_IMAGE_ADVANCEDSERVER):$(MQ_TAG)
|
||||
MQ_IMAGE_DEV_FULL_RELEASE_NAME=$(MQ_IMAGE_DEVSERVER):$(MQ_TAG)
|
||||
|
||||
#setup variables for fat-manifests
|
||||
MQ_IMAGE_DEVSERVER_MANIFEST=$(MQ_IMAGE_DEVSERVER):$(MQ_MANIFEST_TAG)
|
||||
MQ_IMAGE_ADVANCEDSERVER_MANIFEST=$(MQ_IMAGE_ADVANCEDSERVER):$(MQ_MANIFEST_TAG)
|
||||
MQ_IMAGE_DEVSERVER_AMD64=$(MQ_DELIVERY_REGISTRY_FULL_PATH)/$(MQ_IMAGE_DEVSERVER):$(MQ_AMD64_TAG)
|
||||
MQ_IMAGE_DEVSERVER_S390X=$(MQ_DELIVERY_REGISTRY_FULL_PATH)/$(MQ_IMAGE_DEVSERVER):$(MQ_S390X_TAG)
|
||||
MQ_IMAGE_DEVSERVER_PPC64LE=$(MQ_DELIVERY_REGISTRY_FULL_PATH)/$(MQ_IMAGE_DEVSERVER):$(MQ_PPC64LE_TAG)
|
||||
MQ_IMAGE_ADVANCEDSERVER_AMD64=$(MQ_DELIVERY_REGISTRY_FULL_PATH)/$(MQ_IMAGE_ADVANCEDSERVER):$(MQ_AMD64_TAG)
|
||||
MQ_IMAGE_ADVANCEDSERVER_S390X=$(MQ_DELIVERY_REGISTRY_FULL_PATH)/$(MQ_IMAGE_ADVANCEDSERVER):$(MQ_S390X_TAG)
|
||||
MQ_IMAGE_ADVANCEDSERVER_PPC64LE=$(MQ_DELIVERY_REGISTRY_FULL_PATH)/$(MQ_IMAGE_ADVANCEDSERVER):$(MQ_PPC64LE_TAG)
|
||||
|
||||
###############################################################################
|
||||
# Build targets
|
||||
###############################################################################
|
||||
.PHONY: default
|
||||
default: build-devserver
|
||||
|
||||
# Targets default to a RHEL image on a RHEL host, or an Ubuntu image everywhere else
|
||||
# Build all components (except incubating ones)
|
||||
.PHONY: all
|
||||
all: build-devserver build-advancedserver
|
||||
|
||||
.PHONY: build-devserver
|
||||
ifdef RHEL_HOST
|
||||
build-devserver: build-devserver-rhel
|
||||
.PHONY: test-all
|
||||
test-all: build-devjmstest test-devserver test-advancedserver
|
||||
|
||||
.PHONY: devserver
|
||||
devserver: build-devserver build-devjmstest test-devserver
|
||||
|
||||
.PHONY: advancedserver
|
||||
advancedserver: build-advancedserver test-advancedserver
|
||||
|
||||
# Build incubating components
|
||||
.PHONY: incubating
|
||||
incubating: build-explorer
|
||||
|
||||
downloads/$(MQ_ARCHIVE_DEV):
|
||||
$(info $(SPACER)$(shell printf $(TITLE)"Downloading IBM MQ Advanced for Developers "$(MQ_VERSION)$(END)))
|
||||
mkdir -p downloads
|
||||
ifneq "$(BUILD_RSYNC_SERVER)" "$(EMPTY)"
|
||||
# Use key which is not stored in the repository to fetch the files from the fileserver
|
||||
curl --fail --location $(BUILD_RSYNC_ENCRYPTED_KEY_URL) --output ./host.key.gpg
|
||||
@echo $(BUILD_RSYNC_ENCRYPTION_PASSWORD)|gpg --batch --passphrase-fd 0 ./host.key.gpg
|
||||
chmod 600 ./host.key
|
||||
rsync -rv -e "ssh -o BatchMode=yes -q -o StrictHostKeyChecking=no -i ./host.key" --include="*/" --include="*.tar.gz" --exclude="*" $(BUILD_RSYNC_USER)@$(BUILD_RSYNC_SERVER):"$(BUILD_RSYNC_PATH)" downloads/$(MQ_ARCHIVE_DEV)
|
||||
-@rm host.key.gpg host.key
|
||||
else
|
||||
build-devserver: build-devserver-ubuntu
|
||||
ifneq "$(MQ_ARCHIVE_REPOSITORY_DEV)" "$(EMPTY)"
|
||||
curl --fail --user $(MQ_ARCHIVE_REPOSITORY_USER):$(MQ_ARCHIVE_REPOSITORY_CREDENTIAL) --request GET "$(MQ_ARCHIVE_REPOSITORY_DEV)" --output downloads/$(MQ_ARCHIVE_DEV)
|
||||
else
|
||||
curl --fail --location https://public.dhe.ibm.com/ibmdl/export/pub/software/websphere/messaging/mqadv/$(MQ_ARCHIVE_DEV) --output downloads/$(MQ_ARCHIVE_DEV)
|
||||
endif
|
||||
endif
|
||||
|
||||
.PHONY: build-advancedserver
|
||||
ifdef RHEL_HOST
|
||||
build-advancedserver: build-advancedserver-rhel
|
||||
downloads/$(MQ_ARCHIVE):
|
||||
$(info $(SPACER)$(shell printf $(TITLE)"Downloading IBM MQ Advanced "$(MQ_VERSION)$(END)))
|
||||
mkdir -p downloads
|
||||
ifneq "$(BUILD_RSYNC_SERVER)" "$(EMPTY)"
|
||||
# Use key which is not stored in the repository to fetch the files from the fileserver
|
||||
-@rm host.key.gpg host.key
|
||||
curl --fail --location $(BUILD_RSYNC_ENCRYPTED_KEY_URL) --output ./host.key.gpg
|
||||
@echo $(BUILD_RSYNC_ENCRYPTION_PASSWORD)|gpg --batch --passphrase-fd 0 ./host.key.gpg
|
||||
chmod 600 ./host.key
|
||||
rsync -rv -e "ssh -o BatchMode=yes -q -o StrictHostKeyChecking=no -i ./host.key" --include="*/" --include="*.tar.gz" --exclude="*" $(BUILD_RSYNC_USER)@$(BUILD_RSYNC_SERVER):"$(BUILD_RSYNC_PATH)" downloads/$(MQ_ARCHIVE)
|
||||
-@rm host.key.gpg host.key
|
||||
else
|
||||
build-advancedserver: build-advancedserver-ubuntu
|
||||
ifneq "$(MQ_ARCHIVE_REPOSITORY)" "$(EMPTY)"
|
||||
curl --fail --user $(MQ_ARCHIVE_REPOSITORY_USER):$(MQ_ARCHIVE_REPOSITORY_CREDENTIAL) --request GET "$(MQ_ARCHIVE_REPOSITORY)" --output downloads/$(MQ_ARCHIVE)
|
||||
endif
|
||||
endif
|
||||
|
||||
.PHONY: downloads
|
||||
downloads: downloads/$(MQ_ARCHIVE_DEV) downloads/$(MQ_SDK_ARCHIVE)
|
||||
|
||||
.PHONY: test-devserver
|
||||
ifdef RHEL_HOST
|
||||
test-devserver: test-devserver-rhel
|
||||
else
|
||||
test-devserver: test-devserver-ubuntu
|
||||
endif
|
||||
.PHONY: cache-mq-tag
|
||||
cache-mq-tag:
|
||||
@printf "MQ_MANIFEST_TAG=$(MQ_MANIFEST_TAG)\n" | tee $(PATH_TO_MQ_TAG_CACHE)
|
||||
|
||||
###############################################################################
|
||||
# Test targets
|
||||
###############################################################################
|
||||
|
||||
# Vendor Go dependencies for the Docker tests
|
||||
test/docker/vendor:
|
||||
cd test/docker && go mod vendor
|
||||
|
||||
# Shortcut to just run the unit tests
|
||||
.PHONY: test-unit
|
||||
test-unit:
|
||||
$(COMMAND) build --target builder --file Dockerfile-server .
|
||||
|
||||
.PHONY: test-advancedserver
|
||||
ifdef RHEL_HOST
|
||||
test-advancedserver: test-advancedserver-rhel
|
||||
else
|
||||
test-advancedserver: test-advancedserver-ubuntu
|
||||
endif
|
||||
test-advancedserver: test/docker/vendor
|
||||
$(info $(SPACER)$(shell printf $(TITLE)"Test $(MQ_IMAGE_ADVANCEDSERVER):$(MQ_TAG) on $(shell $(COMMAND) --version)"$(END)))
|
||||
$(COMMAND) inspect $(MQ_IMAGE_ADVANCEDSERVER):$(MQ_TAG)
|
||||
cd test/docker && TEST_IMAGE=$(MQ_IMAGE_ADVANCEDSERVER):$(MQ_TAG) EXPECTED_LICENSE=Production DOCKER_API_VERSION=$(DOCKER_API_VERSION) go test -parallel $(NUM_CPU) -timeout $(TEST_TIMEOUT_DOCKER) $(TEST_OPTS_DOCKER)
|
||||
|
||||
.PHONY: build-devjmstest
|
||||
ifdef RHEL_HOST
|
||||
build-devjmstest: build-devjmstest-rhel
|
||||
else
|
||||
build-devjmstest: build-devjmstest-ubuntu
|
||||
build-devjmstest:
|
||||
$(info $(SPACER)$(shell printf $(TITLE)"Build JMS tests for developer config"$(END)))
|
||||
cd test/messaging && docker build --tag $(DEV_JMS_IMAGE) .
|
||||
|
||||
.PHONY: test-devserver
|
||||
test-devserver: test/docker/vendor
|
||||
$(info $(SPACER)$(shell printf $(TITLE)"Test $(MQ_IMAGE_DEVSERVER):$(MQ_TAG) on $(shell $(COMMAND) --version)"$(END)))
|
||||
$(COMMAND) inspect $(MQ_IMAGE_DEVSERVER):$(MQ_TAG)
|
||||
cd test/docker && TEST_IMAGE=$(MQ_IMAGE_DEVSERVER):$(MQ_TAG) EXPECTED_LICENSE=Developer DEV_JMS_IMAGE=$(DEV_JMS_IMAGE) IBMJRE=true DOCKER_API_VERSION=$(DOCKER_API_VERSION) go test -parallel $(NUM_CPU) -timeout $(TEST_TIMEOUT_DOCKER) -tags mqdev $(TEST_OPTS_DOCKER)
|
||||
|
||||
.PHONY: coverage
|
||||
coverage:
|
||||
mkdir coverage
|
||||
|
||||
.PHONY: test-advancedserver-cover
|
||||
test-advancedserver-cover: test/docker/vendor coverage
|
||||
$(info $(SPACER)$(shell printf $(TITLE)"Test $(MQ_IMAGE_ADVANCEDSERVER):$(MQ_TAG) with code coverage on $(shell $(COMMAND) --version)"$(END)))
|
||||
rm -f ./coverage/unit*.cov
|
||||
# Run unit tests with coverage, for each package under 'internal'
|
||||
go list -f '{{.Name}}' ./internal/... | xargs -I {} go test -cover -covermode count -coverprofile ./coverage/unit-{}.cov ./internal/{}
|
||||
# ls -1 ./cmd | xargs -I {} go test -cover -covermode count -coverprofile ./coverage/unit-{}.cov ./cmd/{}/...
|
||||
echo 'mode: count' > ./coverage/unit.cov
|
||||
tail -q -n +2 ./coverage/unit-*.cov >> ./coverage/unit.cov
|
||||
go tool cover -html=./coverage/unit.cov -o ./coverage/unit.html
|
||||
|
||||
rm -f ./test/docker/coverage/*.cov
|
||||
rm -f ./coverage/docker.*
|
||||
mkdir -p ./test/docker/coverage/
|
||||
cd test/docker && TEST_IMAGE=$(MQ_IMAGE_ADVANCEDSERVER):$(MQ_TAG)-cover TEST_COVER=true DOCKER_API_VERSION=$(DOCKER_API_VERSION) go test $(TEST_OPTS_DOCKER)
|
||||
echo 'mode: count' > ./coverage/docker.cov
|
||||
tail -q -n +2 ./test/docker/coverage/*.cov >> ./coverage/docker.cov
|
||||
go tool cover -html=./coverage/docker.cov -o ./coverage/docker.html
|
||||
|
||||
echo 'mode: count' > ./coverage/combined.cov
|
||||
tail -q -n +2 ./coverage/unit.cov ./coverage/docker.cov >> ./coverage/combined.cov
|
||||
go tool cover -html=./coverage/combined.cov -o ./coverage/combined.html
|
||||
|
||||
###############################################################################
|
||||
# Build functions
|
||||
###############################################################################
|
||||
|
||||
# Command to build the image
|
||||
# Args: imageName, imageTag, dockerfile, extraArgs, dockerfileTarget
|
||||
define build-mq-command
|
||||
$(COMMAND) build \
|
||||
--tag $1:$2 \
|
||||
--file $3 \
|
||||
$4 \
|
||||
--build-arg IMAGE_REVISION="$(IMAGE_REVISION)" \
|
||||
--build-arg IMAGE_SOURCE="$(IMAGE_SOURCE)" \
|
||||
--build-arg IMAGE_TAG="$1:$2" \
|
||||
--label version=$(MQ_VERSION) \
|
||||
--label name=$1 \
|
||||
--label build-date=$(shell date +%Y-%m-%dT%H:%M:%S%z) \
|
||||
--label architecture="$(ARCH)" \
|
||||
--label run="podman run -d -e LICENSE=accept $1:$2" \
|
||||
--label vcs-ref=$(IMAGE_REVISION) \
|
||||
--label vcs-type=git \
|
||||
--label vcs-url=$(IMAGE_SOURCE) \
|
||||
$(EXTRA_LABELS) \
|
||||
--target $5 \
|
||||
.
|
||||
endef
|
||||
|
||||
# Build using a separate container to host the MQ download files.
|
||||
# To minimize the layers in the resulting image, the download files can't be part of the build context.
|
||||
# The "docker build" command (and "podman build" on macOS) don't allow you to mount a directory into the build, so a
|
||||
# separate container is used to host a web server.
|
||||
# Note that for Podman, this means that you need to be using the "rootful" mode, because the rootless mode doesn't allow
|
||||
# much control of networking, so the containers can't talk to each other.
|
||||
define build-mq-using-web-server
|
||||
$(COMMAND) network create $(BUILD_SERVER_NETWORK)
|
||||
$(COMMAND) run \
|
||||
--rm \
|
||||
--name $(BUILD_SERVER_CONTAINER) \
|
||||
--network $(BUILD_SERVER_NETWORK) \
|
||||
--volume $(DOWNLOADS_DIR):/opt/app-root/src$(VOLUME_MOUNT_OPTIONS) \
|
||||
--detach \
|
||||
registry.access.redhat.com/ubi8/nginx-120 nginx -g "daemon off;" || ($(COMMAND) network rm $(BUILD_SERVER_NETWORK) && exit 1)
|
||||
BUILD_SERVER_IP=$$($(COMMAND) inspect -f '{{ .NetworkSettings.Networks.$(BUILD_SERVER_NETWORK).IPAddress }}' $(BUILD_SERVER_CONTAINER)); \
|
||||
DOCKER_BUILDKIT=0 $(call build-mq-command,$1,$2,$3,--network build --build-arg MQ_URL=http://$$BUILD_SERVER_IP:8080/$4,$5) || ($(COMMAND) rm -f $(BUILD_SERVER_CONTAINER) && $(COMMAND) network rm $(BUILD_SERVER_NETWORK) && exit 1)
|
||||
$(COMMAND) rm -f $(BUILD_SERVER_CONTAINER)
|
||||
$(COMMAND) network rm $(BUILD_SERVER_NETWORK)
|
||||
endef
|
||||
|
||||
# When building with Docker, always use the web server build because you can't use bind-mounted volumes.
|
||||
# Args: imageName, imageTag, dockerfile, mqArchive, dockerfileTarget
|
||||
define build-mq-docker
|
||||
$(call build-mq-using-web-server,$1,$2,$3,$4,$5)
|
||||
endef
|
||||
|
||||
# Make sure we don't use VOLUME_MOUNT_OPTIONS for Podman on macOS
|
||||
ifeq "$(COMMAND)" "podman"
|
||||
ifeq "$(shell uname -s)" "Darwin"
|
||||
VOLUME_MOUNT_OPTIONS:=
|
||||
endif
|
||||
endif
|
||||
|
||||
# UBUNTU building targets
|
||||
.PHONY: build-devserver-ubuntu
|
||||
build-devserver-ubuntu:
|
||||
$(MAKE) -f Makefile-UBUNTU build-devserver
|
||||
# When building with Podman on macOS (Darwin), use the web server build because you can't use bind-mounted volumes with `podman build` on macOS
|
||||
# Args: imageName, imageTag, dockerfile, mqArchive, dockerfileTarget
|
||||
define build-mq-podman-Darwin
|
||||
$(call build-mq-using-web-server,$1,$2,$3,$4,$5)
|
||||
endef
|
||||
|
||||
.PHONY: test-devserver-ubuntu
|
||||
test-devserver-ubuntu:
|
||||
$(MAKE) -f Makefile-UBUNTU test-devserver
|
||||
# When building with Podman on Linux, just pass the downloads directory as a volume
|
||||
# Args: imageName, imageTag, dockerfile, mqArchive, dockerfileTarget
|
||||
define build-mq-podman-Linux
|
||||
$(call build-mq-command,$1,$2,$3,--volume $(DOWNLOADS_DIR):/var/downloads$(VOLUME_MOUNT_OPTIONS) --build-arg MQ_URL=file:///var/downloads/$4,$5)
|
||||
endef
|
||||
|
||||
.PHONY: build-devjmstest-ubuntu
|
||||
$(MAKE) -f Makefile-UBUNTU build-devjmstest
|
||||
# When building with Podman, just pass the downloads directory as a volume
|
||||
# Args: imageName, imageTag, dockerfile, mqArchive, dockerfileTarget
|
||||
define build-mq-podman
|
||||
$(call build-mq-podman-$(shell uname -s),$1,$2,$3,$4,$5)
|
||||
endef
|
||||
|
||||
.PHONY: build-advancedserver-ubuntu
|
||||
build-advancedserver-ubuntu:
|
||||
$(MAKE) -f Makefile-UBUNTU build-advancedserver
|
||||
# Build an MQ image. The commands used are slightly different between Docker and Podman
|
||||
# Args: imageName, imageTag, dockerfile, mqArchive, dockerfileTarget
|
||||
define build-mq
|
||||
$(call build-mq-$(COMMAND),$1,$2,$3,$4,$5)
|
||||
endef
|
||||
|
||||
.PHONY: test-advancedserver-ubuntu
|
||||
test-advancedserver-ubuntu:
|
||||
$(MAKE) -f Makefile-UBUNTU test-advancedserver
|
||||
###############################################################################
|
||||
# Build targets
|
||||
###############################################################################
|
||||
.PHONY: build-advancedserver-host
|
||||
build-advancedserver-host: build-advancedserver
|
||||
|
||||
.PHONY: build-devjmstest-ubuntu
|
||||
build-devjmstest-ubuntu:
|
||||
$(MAKE) -f Makefile-UBUNTU build-devjmstest
|
||||
.PHONY: build-advancedserver
|
||||
build-advancedserver: log-build-env downloads/$(MQ_ARCHIVE) command-version
|
||||
$(info $(SPACER)$(shell printf $(TITLE)"Build $(MQ_IMAGE_ADVANCEDSERVER):$(MQ_TAG)"$(END)))
|
||||
$(call build-mq,$(MQ_IMAGE_ADVANCEDSERVER),$(MQ_TAG),Dockerfile-server,$(MQ_ARCHIVE),mq-server)
|
||||
|
||||
# RHEL building targets
|
||||
.PHONY: build-devserver-rhel
|
||||
build-devserver-rhel:
|
||||
$(MAKE) -f Makefile-RHEL build-devserver
|
||||
.PHONY: build-devserver-host
|
||||
build-devserver-host: build-devserver
|
||||
|
||||
.PHONY: test-devserver-rhel
|
||||
test-devserver-rhel:
|
||||
$(MAKE) -f Makefile-RHEL test-devserver
|
||||
.PHONY: build-devserver
|
||||
build-devserver: log-build-env downloads/$(MQ_ARCHIVE_DEV) command-version
|
||||
$(info $(shell printf $(TITLE)"Build $(MQ_IMAGE_DEVSERVER):$(MQ_TAG)"$(END)))
|
||||
$(call build-mq,$(MQ_IMAGE_DEVSERVER),$(MQ_TAG),Dockerfile-server,$(MQ_ARCHIVE_DEV),mq-dev-server)
|
||||
|
||||
.PHONY: build-advancedserver-rhel
|
||||
build-advancedserver-rhel:
|
||||
$(MAKE) -f Makefile-RHEL build-advancedserver
|
||||
.PHONY: build-advancedserver-cover
|
||||
build-advancedserver-cover: command-version
|
||||
$(COMMAND) build --build-arg BASE_IMAGE=$(MQ_IMAGE_ADVANCEDSERVER):$(MQ_TAG) -t $(MQ_IMAGE_ADVANCEDSERVER):$(MQ_TAG)-cover -f Dockerfile-server.cover .
|
||||
|
||||
.PHONY: test-advancedserver-rhel
|
||||
test-advancedserver-rhel:
|
||||
$(MAKE) -f Makefile-RHEL test-advancedserver
|
||||
.PHONY: build-explorer
|
||||
build-explorer: downloads/$(MQ_ARCHIVE_DEV)
|
||||
$(call build-mq,mq-explorer,latest-$(ARCH),incubating/mq-explorer/Dockerfile,$(MQ_ARCHIVE_DEV),mq-explorer)
|
||||
|
||||
.PHONY: build-devjmstest-rhel
|
||||
build-devjmstest-rhel:
|
||||
$(MAKE) -f Makefile-RHEL build-devjmstest
|
||||
.PHONY: build-sdk
|
||||
build-sdk: downloads/$(MQ_ARCHIVE_DEV)
|
||||
$(info $(shell printf $(TITLE)"Build $(MQ_IMAGE_SDK)"$(END)))
|
||||
$(call build-mq,mq-sdk,$(MQ_TAG),incubating/mq-sdk/Dockerfile,$(MQ_SDK_ARCHIVE),mq-sdk)
|
||||
|
||||
###############################################################################
|
||||
# Logging targets
|
||||
###############################################################################
|
||||
.PHONY: log-build-env
|
||||
log-build-vars:
|
||||
$(info $(SPACER)$(shell printf $(TITLE)"Build environment"$(END)))
|
||||
@echo ARCH=$(ARCH)
|
||||
@echo MQ_VERSION=$(MQ_VERSION)
|
||||
@echo MQ_ARCHIVE=$(MQ_ARCHIVE)
|
||||
@echo MQ_ARCHIVE_DEV=$(MQ_ARCHIVE_DEV)
|
||||
@echo MQ_IMAGE_DEVSERVER=$(MQ_IMAGE_DEVSERVER)
|
||||
@echo MQ_IMAGE_ADVANCEDSERVER=$(MQ_IMAGE_ADVANCEDSERVER)
|
||||
@echo COMMAND=$(COMMAND)
|
||||
|
||||
.PHONY: log-build-env
|
||||
log-build-env: log-build-vars
|
||||
$(info $(SPACER)$(shell printf $(TITLE)"Build environment - $(COMMAND) info"$(END)))
|
||||
@echo Command version: $(shell $(COMMAND) --version)
|
||||
$(COMMAND) info
|
||||
|
||||
include formatting.mk
|
||||
|
||||
###############################################################################
|
||||
# Push/pull targets
|
||||
###############################################################################
|
||||
.PHONY: pull-mq-archive
|
||||
pull-mq-archive:
|
||||
curl --fail --user $(MQ_ARCHIVE_REPOSITORY_USER):$(MQ_ARCHIVE_REPOSITORY_CREDENTIAL) --request GET "$(MQ_ARCHIVE_REPOSITORY)" --output downloads/$(MQ_ARCHIVE)
|
||||
|
||||
.PHONY: pull-mq-archive-dev
|
||||
pull-mq-archive-dev:
|
||||
curl --fail --user $(MQ_ARCHIVE_REPOSITORY_USER):$(MQ_ARCHIVE_REPOSITORY_CREDENTIAL) --request GET "$(MQ_ARCHIVE_REPOSITORY_DEV)" --output downloads/$(MQ_ARCHIVE_DEV)
|
||||
|
||||
.PHONY: push-advancedserver
|
||||
push-advancedserver:
|
||||
@if [ $(MQ_DELIVERY_REGISTRY_NAMESPACE) = "master-fake" ]; then\
|
||||
echo "Detected fake master build. Note that the push destination is set to the fake master namespace: $(MQ_DELIVERY_REGISTRY_FULL_PATH)";\
|
||||
fi
|
||||
$(info $(SPACER)$(shell printf $(TITLE)"Push production image to $(MQ_DELIVERY_REGISTRY_FULL_PATH)"$(END)))
|
||||
$(COMMAND) login $(MQ_DELIVERY_REGISTRY_HOSTNAME) -u $(MQ_DELIVERY_REGISTRY_USER) -p $(MQ_DELIVERY_REGISTRY_CREDENTIAL)
|
||||
$(COMMAND) tag $(MQ_IMAGE_ADVANCEDSERVER)\:$(MQ_TAG) $(MQ_DELIVERY_REGISTRY_FULL_PATH)/$(MQ_IMAGE_FULL_RELEASE_NAME)
|
||||
$(COMMAND) push $(MQ_DELIVERY_REGISTRY_FULL_PATH)/$(MQ_IMAGE_FULL_RELEASE_NAME)
|
||||
|
||||
.PHONY: push-devserver
|
||||
push-devserver:
|
||||
@if [ $(MQ_DELIVERY_REGISTRY_NAMESPACE) = "master-fake" ]; then\
|
||||
echo "Detected fake master build. Note that the push destination is set to the fake master namespace: $(MQ_DELIVERY_REGISTRY_FULL_PATH)";\
|
||||
fi
|
||||
$(info $(SPACER)$(shell printf $(TITLE)"Push developer image to $(MQ_DELIVERY_REGISTRY_FULL_PATH)"$(END)))
|
||||
$(COMMAND) login $(MQ_DELIVERY_REGISTRY_HOSTNAME) -u $(MQ_DELIVERY_REGISTRY_USER) -p $(MQ_DELIVERY_REGISTRY_CREDENTIAL)
|
||||
$(COMMAND) tag $(MQ_IMAGE_DEVSERVER)\:$(MQ_TAG) $(MQ_DELIVERY_REGISTRY_FULL_PATH)/$(MQ_IMAGE_DEV_FULL_RELEASE_NAME)
|
||||
$(COMMAND) push $(MQ_DELIVERY_REGISTRY_FULL_PATH)/$(MQ_IMAGE_DEV_FULL_RELEASE_NAME)
|
||||
|
||||
.PHONY: pull-advancedserver
|
||||
pull-advancedserver:
|
||||
$(info $(SPACER)$(shell printf $(TITLE)"Pull production image from $(MQ_DELIVERY_REGISTRY_FULL_PATH)"$(END)))
|
||||
$(COMMAND) login $(MQ_DELIVERY_REGISTRY_HOSTNAME) -u $(MQ_DELIVERY_REGISTRY_USER) -p $(MQ_DELIVERY_REGISTRY_CREDENTIAL)
|
||||
$(COMMAND) pull $(MQ_DELIVERY_REGISTRY_FULL_PATH)/$(MQ_IMAGE_FULL_RELEASE_NAME)
|
||||
$(COMMAND) tag $(MQ_DELIVERY_REGISTRY_FULL_PATH)/$(MQ_IMAGE_FULL_RELEASE_NAME) $(MQ_IMAGE_ADVANCEDSERVER)\:$(MQ_TAG)
|
||||
|
||||
.PHONY: pull-devserver
|
||||
pull-devserver:
|
||||
$(info $(SPACER)$(shell printf $(TITLE)"Pull developer image from $(MQ_DELIVERY_REGISTRY_FULL_PATH)"$(END)))
|
||||
$(COMMAND) login $(MQ_DELIVERY_REGISTRY_HOSTNAME) -u $(MQ_DELIVERY_REGISTRY_USER) -p $(MQ_DELIVERY_REGISTRY_CREDENTIAL)
|
||||
$(COMMAND) pull $(MQ_DELIVERY_REGISTRY_FULL_PATH)/$(MQ_IMAGE_DEV_FULL_RELEASE_NAME)
|
||||
$(COMMAND) tag $(MQ_DELIVERY_REGISTRY_FULL_PATH)/$(MQ_IMAGE_DEV_FULL_RELEASE_NAME) $(MQ_IMAGE_DEVSERVER)\:$(MQ_TAG)
|
||||
|
||||
.PHONY: push-manifest
|
||||
push-manifest: build-skopeo-container
|
||||
$(info $(SPACER)$(shell printf $(TITLE)"** Determining the image digests **"$(END)))
|
||||
ifneq "$(LTS)" "true"
|
||||
$(eval MQ_IMAGE_DEVSERVER_AMD64_DIGEST=$(shell $(COMMAND) run skopeo:latest --override-os linux inspect --creds $(MQ_ARCHIVE_REPOSITORY_USER):$(MQ_ARCHIVE_REPOSITORY_CREDENTIAL) docker://$(MQ_IMAGE_DEVSERVER_AMD64) | jq -r .Digest))
|
||||
$(eval MQ_IMAGE_DEVSERVER_S390X_DIGEST=$(shell $(COMMAND) run skopeo:latest --override-os linux inspect --creds $(MQ_ARCHIVE_REPOSITORY_USER):$(MQ_ARCHIVE_REPOSITORY_CREDENTIAL) docker://$(MQ_IMAGE_DEVSERVER_S390X) | jq -r .Digest))
|
||||
$(eval MQ_IMAGE_DEVSERVER_PPC64LE_DIGEST=$(shell $(COMMAND) run skopeo:latest --override-os linux inspect --creds $(MQ_ARCHIVE_REPOSITORY_USER):$(MQ_ARCHIVE_REPOSITORY_CREDENTIAL) docker://$(MQ_IMAGE_DEVSERVER_PPC64LE) | jq -r .Digest))
|
||||
$(info $(shell printf "** Determined the built $(MQ_IMAGE_DEVSERVER_AMD64) has a digest of $(MQ_IMAGE_DEVSERVER_AMD64_DIGEST)**"$(END)))
|
||||
$(info $(shell printf "** Determined the built $(MQ_IMAGE_DEVSERVER_S390X) has a digest of $(MQ_IMAGE_DEVSERVER_S390X_DIGEST)**"$(END)))
|
||||
$(info $(shell printf "** Determined the built $(MQ_IMAGE_DEVSERVER_PPC64LE) has a digest of $(MQ_IMAGE_DEVSERVER_PPC64LE_DIGEST)**"$(END)))
|
||||
endif
|
||||
$(eval MQ_IMAGE_ADVANCEDSERVER_AMD64_DIGEST=$(shell $(COMMAND) run skopeo:latest --override-os linux inspect --creds $(MQ_ARCHIVE_REPOSITORY_USER):$(MQ_ARCHIVE_REPOSITORY_CREDENTIAL) docker://$(MQ_IMAGE_ADVANCEDSERVER_AMD64) | jq -r .Digest))
|
||||
$(eval MQ_IMAGE_ADVANCEDSERVER_S390X_DIGEST=$(shell $(COMMAND) run skopeo:latest --override-os linux inspect --creds $(MQ_ARCHIVE_REPOSITORY_USER):$(MQ_ARCHIVE_REPOSITORY_CREDENTIAL) docker://$(MQ_IMAGE_ADVANCEDSERVER_S390X) | jq -r .Digest))
|
||||
$(eval MQ_IMAGE_ADVANCEDSERVER_PPC64LE_DIGEST=$(shell $(COMMAND) run skopeo:latest --override-os linux inspect --creds $(MQ_ARCHIVE_REPOSITORY_USER):$(MQ_ARCHIVE_REPOSITORY_CREDENTIAL) docker://$(MQ_IMAGE_ADVANCEDSERVER_PPC64LE) | jq -r .Digest))
|
||||
$(info $(shell printf "** Determined the built $(MQ_IMAGE_ADVANCEDSERVER_AMD64) has a digest of $(MQ_IMAGE_ADVANCEDSERVER_AMD64_DIGEST)**"$(END)))
|
||||
$(info $(shell printf "** Determined the built $(MQ_IMAGE_ADVANCEDSERVER_S390X) has a digest of $(MQ_IMAGE_ADVANCEDSERVER_S390X_DIGEST)**"$(END)))
|
||||
$(info $(shell printf "** Determined the built $(MQ_IMAGE_ADVANCEDSERVER_PPC64LE) has a digest of $(MQ_IMAGE_ADVANCEDSERVER_PPC64LE_DIGEST)**"$(END)))
|
||||
ifneq "$(LTS)" "true"
|
||||
$(info $(shell printf "** Calling script to create fat-manifest for $(MQ_IMAGE_DEVSERVER_MANIFEST)**"$(END)))
|
||||
echo $(shell ./travis-build-scripts/create-manifest-list.sh -r $(MQ_DELIVERY_REGISTRY_HOSTNAME) -n $(MQ_DELIVERY_REGISTRY_NAMESPACE) -i $(MQ_IMAGE_DEVSERVER) -t $(MQ_MANIFEST_TAG) -u $(MQ_ARCHIVE_REPOSITORY_USER) -p $(MQ_ARCHIVE_REPOSITORY_CREDENTIAL) -d "$(MQ_IMAGE_DEVSERVER_AMD64_DIGEST) $(MQ_IMAGE_DEVSERVER_S390X_DIGEST) $(MQ_IMAGE_DEVSERVER_PPC64LE_DIGEST)" $(END))
|
||||
endif
|
||||
$(info $(shell printf "** Calling script to create fat-manifest for $(MQ_IMAGE_ADVANCEDSERVER_MANIFEST)**"$(END)))
|
||||
echo $(shell ./travis-build-scripts/create-manifest-list.sh -r $(MQ_DELIVERY_REGISTRY_HOSTNAME) -n $(MQ_DELIVERY_REGISTRY_NAMESPACE) -i $(MQ_IMAGE_ADVANCEDSERVER) -t $(MQ_MANIFEST_TAG) -u $(MQ_ARCHIVE_REPOSITORY_USER) -p $(MQ_ARCHIVE_REPOSITORY_CREDENTIAL) -d "$(MQ_IMAGE_ADVANCEDSERVER_AMD64_DIGEST) $(MQ_IMAGE_ADVANCEDSERVER_S390X_DIGEST) $(MQ_IMAGE_ADVANCEDSERVER_PPC64LE_DIGEST)" $(END))
|
||||
|
||||
.PHONY: build-skopeo-container
|
||||
build-skopeo-container:
|
||||
$(COMMAND) images | grep -q "skopeo"; if [ $$? != 0 ]; then $(COMMAND) build -t skopeo:latest ./docker-builds/skopeo/; fi
|
||||
|
||||
###############################################################################
|
||||
# Other targets
|
||||
###############################################################################
|
||||
|
||||
# Common targets
|
||||
.PHONY: clean
|
||||
clean:
|
||||
rm -rf ./coverage
|
||||
rm -rf ./build
|
||||
rm -rf ./deps
|
||||
|
||||
.PHONY: deps
|
||||
deps:
|
||||
glide install --strip-vendor
|
||||
.PHONY: install-build-deps
|
||||
install-build-deps:
|
||||
ARCH=$(ARCH) ./install-build-deps.sh
|
||||
|
||||
.PHONY: install-credential-helper
|
||||
install-credential-helper:
|
||||
ifeq ($(ARCH),amd64)
|
||||
ARCH=$(ARCH) ./travis-build-scripts/install-credential-helper.sh
|
||||
endif
|
||||
|
||||
.PHONY: build-cov
|
||||
build-cov:
|
||||
@@ -142,34 +565,42 @@ lint: $(addsuffix /$(wildcard *.go), $(GO_PKG_DIRS))
|
||||
golint -set_exit_status $(sort $(dir $(wildcard $(addsuffix /*/*.go, $(GO_PKG_DIRS)))))
|
||||
|
||||
.PHONY: gosec
|
||||
gosec: $(info $(SPACER)$(shell printf "Running gosec test"$(END)))
|
||||
@gosec -fmt=json -out=gosec_results.json cmd/... internal/... 2> /dev/null ;\
|
||||
cat "gosec_results.json" ;\
|
||||
cat gosec_results.json | grep HIGH | grep severity > /dev/null ;\
|
||||
if [ $$? -eq 0 ]; then \
|
||||
printf "\nFAILURE: gosec found files containing HIGH severity issues - see results.json\n" ;\
|
||||
gosec:
|
||||
$(info $(SPACER)$(shell printf "Running gosec test"$(END)))
|
||||
@gosecrc=0; gosec -fmt=json -out=gosec_results.json cmd/... internal/... 2> /dev/null || gosecrc=$$?; \
|
||||
cat gosec_results.json | jq '{"GolangErrors": (.["Golang errors"]|length>0),"Issues":(.Issues|length>0)}' | grep 'true' >/dev/null ;\
|
||||
if [ $$? -eq 0 ] || [ $$gosecrc -ne 0 ]; then \
|
||||
printf "FAILURE: Issues found running gosec - see gosec_results.json\n" ;\
|
||||
cat "gosec_results.json" ;\
|
||||
exit 1 ;\
|
||||
else \
|
||||
printf "\ngosec found no HIGH severity issues\n" ;\
|
||||
fi ;\
|
||||
cat gosec_results.json | grep MEDIUM | grep severity > /dev/null ;\
|
||||
if [ $$? -eq 0 ]; then \
|
||||
printf "\nFAILURE: gosec found files containing MEDIUM severity issues - see results.json\n" ;\
|
||||
exit 1 ;\
|
||||
else \
|
||||
printf "\ngosec found no MEDIUM severity issues\n" ;\
|
||||
fi ;\
|
||||
cat gosec_results.json | grep LOW | grep severity > /dev/null;\
|
||||
if [ $$? -eq 0 ]; then \
|
||||
printf "\nFAILURE: gosec found files containing LOW severity issues - see results.json\n" ;\
|
||||
exit 1;\
|
||||
else \
|
||||
printf "\ngosec found no LOW severity issues\n" ;\
|
||||
fi ;\
|
||||
printf "gosec found no issues\n" ;\
|
||||
cat "gosec_results.json" ;\
|
||||
fi
|
||||
|
||||
.PHONY: unknownos
|
||||
unknownos:
|
||||
$(info $(SPACER)$(shell printf "ERROR: Unknown OS ("$(BASE_OS)") please run specific make targets"$(END)))
|
||||
exit 1
|
||||
.PHONY: update-release-information
|
||||
update-release-information:
|
||||
sed -i.bak 's/ARG MQ_URL=.*-LinuxX64.tar.gz"/ARG MQ_URL="https:\/\/public.dhe.ibm.com\/ibmdl\/export\/pub\/software\/websphere\/messaging\/mqadv\/$(MQ_VERSION)-IBM-MQ-Advanced-for-Developers-Non-Install-LinuxX64.tar.gz"/g' Dockerfile-server && rm Dockerfile-server.bak
|
||||
$(eval MQ_VERSION_1=$(shell echo '${MQ_VERSION}' | rev | cut -c 3- | rev))
|
||||
sed -i.bak 's/IBM_MQ_.*_LINUX_X86-64_NOINST.tar.gz/IBM_MQ_${MQ_VERSION_1}_LINUX_X86-64_NOINST.tar.gz/g' docs/building.md && rm docs/building.md.bak
|
||||
sed -i.bak 's/ibm-mqadvanced-server:.*-amd64/ibm-mqadvanced-server:$(MQ_VERSION)-amd64/g' docs/security.md
|
||||
sed -i.bak 's/ibm-mqadvanced-server-dev.*-amd64/ibm-mqadvanced-server-dev:$(MQ_VERSION)-amd64/g' docs/security.md && rm docs/security.md.bak
|
||||
sed -i.bak 's/MQ_IMAGE_ADVANCEDSERVER=ibm-mqadvanced-server:.*-amd64/MQ_IMAGE_ADVANCEDSERVER=ibm-mqadvanced-server:$(MQ_VERSION)-amd64/g' docs/testing.md && rm docs/testing.md.bak
|
||||
$(eval MQ_VERSION_2=$(shell echo '${MQ_VERSION_1}' | rev | cut -c 3- | rev))
|
||||
sed -i.bak 's/knowledgecenter\/SSFKSJ_.*\/com/knowledgecenter\/SSFKSJ_${MQ_VERSION_2}.0\/com/g' docs/usage.md && rm docs/usage.md.bak
|
||||
$(eval MQ_VERSION_3=$(shell echo '${MQ_VERSION_1}' | sed "s/\.//g"))
|
||||
sed -i.bak 's/MQ_..._ARCHIVE_REPOSITORY/MQ_${MQ_VERSION_3}_ARCHIVE_REPOSITORY/g' .travis.yml && rm .travis.yml.bak
|
||||
|
||||
include formatting.mk
|
||||
COMMAND_SERVER_VERSION=$(shell $(COMMAND) version --format "{{ .Server.Version }}")
|
||||
COMMAND_CLIENT_VERSION=$(shell $(COMMAND) version --format "{{ .Client.Version }}")
|
||||
PODMAN_VERSION=$(shell podman version --format "{{ .Version }}")
|
||||
.PHONY: command-version
|
||||
command-version:
|
||||
# If we're using Docker, then check it's recent enough to support multi-stage builds
|
||||
ifneq (,$(findstring docker,$(COMMAND)))
|
||||
@test "$(word 1,$(subst ., ,$(COMMAND_CLIENT_VERSION)))" -ge "17" || ("$(word 1,$(subst ., ,$(COMMAND_CLIENT_VERSION)))" -eq "17" && "$(word 2,$(subst ., ,$(COMMAND_CLIENT_VERSION)))" -ge "05") || (echo "Error: Docker client 17.05 or greater is required" && exit 1)
|
||||
@test "$(word 1,$(subst ., ,$(COMMAND_SERVER_VERSION)))" -ge "17" || ("$(word 1,$(subst ., ,$(COMMAND_SERVER_VERSION)))" -eq "17" && "$(word 2,$(subst ., ,$(COMMAND_CLIENT_VERSION)))" -ge "05") || (echo "Error: Docker server 17.05 or greater is required" && exit 1)
|
||||
endif
|
||||
ifneq (,$(findstring podman,$(COMMAND)))
|
||||
@test "$(word 1,$(subst ., ,$(PODMAN_VERSION)))" -ge "1" || (echo "Error: Podman version 1.0 or greater is required" && exit 1)
|
||||
endif
|
||||
|
||||
204
Makefile-RHEL
204
Makefile-RHEL
@@ -1,204 +0,0 @@
|
||||
# © Copyright IBM Corporation 2018, 2019
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
###############################################################################
|
||||
# Conditional variables - you can override the values of these variables from
|
||||
# the command line
|
||||
###############################################################################
|
||||
# BASE_IMAGE is the base image to use for MQ, for example "ubuntu" or "rhel"
|
||||
BASE_IMAGE ?= rhel
|
||||
# MQ_VERSION is the fully qualified MQ version number to build
|
||||
MQ_VERSION ?= 9.1.2.0
|
||||
# MQ_ARCHIVE is the name of the file, under the downloads directory, from which MQ Advanced can
|
||||
# be installed. The default value is derived from MQ_VERSION, BASE_IMAGE and architecture
|
||||
# Does not apply to MQ Advanced for Developers.
|
||||
MQ_ARCHIVE ?= IBM_MQ_$(MQ_VERSION_VRM)_LINUX_$(MQ_ARCHIVE_ARCH).tar.gz
|
||||
# MQ_ARCHIVE_DEV is the name of the file, under the downloads directory, from which MQ Advanced
|
||||
# for Developers can be installed
|
||||
MQ_ARCHIVE_DEV ?= $(MQ_ARCHIVE_DEV_$(MQ_VERSION))
|
||||
# MQ_SDK_ARCHIVE specifies the archive to use for the MQ redistributable client, which is used for building the golang programs.
|
||||
MQ_SDK_ARCHIVE ?= 9.1.2.0-IBM-MQC-Redist-LinuxX64.tar.gz
|
||||
# Options to `go test` for the Docker tests
|
||||
TEST_OPTS_DOCKER ?=
|
||||
# MQ_IMAGE_ADVANCEDSERVER is the name and tag of the built MQ Advanced image
|
||||
MQ_IMAGE_ADVANCEDSERVER ?=mqadvanced-server:$(MQ_VERSION)-RHEL-$(ARCH)
|
||||
# MQ_IMAGE_DEVSERVER is the name and tag of the built MQ Advanced for Developers image
|
||||
MQ_IMAGE_DEVSERVER ?=mqadvanced-server-dev:$(MQ_VERSION)-RHEL-$(ARCH)
|
||||
# MQ_IMAGE_SDK is the name and tag of the built MQ Advanced for Developers SDK image
|
||||
MQ_IMAGE_SDK ?=mq-sdk:$(MQ_VERSION)-$(ARCH)-$(BASE_IMAGE_TAG)
|
||||
# MQ_IMAGE_GOLANG_SDK is the name and tag of the built MQ Advanced for Developers SDK image, plus Go tools
|
||||
MQ_IMAGE_GOLANG_SDK ?=mq-golang-sdk:$(MQ_VERSION)-$(ARCH)-$(BASE_IMAGE_TAG)
|
||||
# MQ_PACKAGES specifies the MQ packages to install. Defaults vary on base image.
|
||||
MQ_PACKAGES ?= MQSeriesRuntime-*.rpm MQSeriesServer-*.rpm MQSeriesJava*.rpm MQSeriesJRE*.rpm MQSeriesGSKit*.rpm MQSeriesMsg*.rpm MQSeriesSamples*.rpm MQSeriesAMS-*.rpm
|
||||
|
||||
###############################################################################
|
||||
# Other variables
|
||||
###############################################################################
|
||||
# ARCH is the platform architecture (e.g. x86_64, ppc64le or s390x)
|
||||
ARCH = $(shell uname -m)
|
||||
# BASE_IMAGE_TAG is a normalized version of BASE_IMAGE, suitable for use in a Docker tag
|
||||
BASE_IMAGE_TAG=$(subst /,-,$(subst :,-,$(BASE_IMAGE)))
|
||||
MQ_IMAGE_DEVSERVER_BASE=mqadvanced-server-dev-base:$(MQ_VERSION)-$(ARCH)-$(BASE_IMAGE_TAG)
|
||||
# Docker image name to use for JMS tests
|
||||
DEV_JMS_IMAGE=mq-dev-jms-test:latest
|
||||
# Variables for versioning
|
||||
IMAGE_REVISION=$(shell git rev-parse HEAD)
|
||||
IMAGE_SOURCE=$(shell git config --get remote.origin.url)
|
||||
MQDEV=
|
||||
EMPTY:=
|
||||
SPACE:= $(EMPTY) $(EMPTY)
|
||||
# MQ_VERSION_VRM is MQ_VERSION with only the Version, Release and Modifier fields (no Fix field). e.g. 9.1.2 instead of 9.1.2.0
|
||||
MQ_VERSION_VRM=$(subst $(SPACE),.,$(wordlist 1,3,$(subst .,$(SPACE),$(MQ_VERSION))))
|
||||
|
||||
|
||||
ifneq (,$(findstring Microsoft,$(shell uname -r)))
|
||||
DOWNLOADS_DIR=$(patsubst /mnt/c%,C:%,$(realpath ./downloads/))
|
||||
else
|
||||
DOWNLOADS_DIR=$(realpath ./downloads/)
|
||||
endif
|
||||
|
||||
# Try to figure out which archive to use from the architecture
|
||||
ifeq "$(ARCH)" "x86_64"
|
||||
MQ_ARCHIVE_ARCH=X86-64
|
||||
MQ_DEV_ARCH=x86-64
|
||||
else ifeq "$(ARCH)" "ppc64le"
|
||||
MQ_ARCHIVE_ARCH=LE_POWER
|
||||
MQ_DEV_ARCH=ppcle
|
||||
else ifeq "$(ARCH)" "s390x"
|
||||
MQ_ARCHIVE_ARCH=SYSTEM_Z
|
||||
MQ_DEV_ARCH=s390x
|
||||
endif
|
||||
# Archive names for IBM MQ Advanced for Developers
|
||||
MQ_ARCHIVE_DEV_9.1.0.0=mqadv_dev910_linux_$(MQ_DEV_ARCH).tar.gz
|
||||
MQ_ARCHIVE_DEV_9.1.1.0=mqadv_dev911_linux_$(MQ_DEV_ARCH).tar.gz
|
||||
MQ_ARCHIVE_DEV_9.1.2.0=mqadv_dev912_linux_$(MQ_DEV_ARCH).tar.gz
|
||||
|
||||
###############################################################################
|
||||
# Build targets
|
||||
###############################################################################
|
||||
.PHONY: vars
|
||||
vars:
|
||||
#ifeq "$(findstring ubuntu,$(BASE_IMAGE))","ubuntu"
|
||||
@echo $(MQ_ARCHIVE_ARCH)
|
||||
@echo $(MQ_ARCHIVE_TYPE)
|
||||
@echo $(MQ_ARCHIVE)
|
||||
|
||||
.PHONY: default
|
||||
default: build-devserver test-devserver
|
||||
|
||||
# Build all components (except incubating ones)
|
||||
.PHONY: all
|
||||
all: build-devserver build-advancedserver
|
||||
|
||||
.PHONY: test-all
|
||||
test-all: build-devjmstest test-devserver test-advancedserver
|
||||
|
||||
.PHONY: devserver
|
||||
devserver: build-devserver build-devjmstest test-devserver
|
||||
|
||||
# Build incubating components
|
||||
.PHONY: incubating
|
||||
incubating: build-explorer
|
||||
|
||||
downloads/$(MQ_ARCHIVE_DEV):
|
||||
$(info $(SPACER)$(shell printf $(TITLE)"Downloading IBM MQ Advanced for Developers "$(MQ_VERSION)$(END)))
|
||||
mkdir -p downloads
|
||||
cd downloads; curl -LO https://public.dhe.ibm.com/ibmdl/export/pub/software/websphere/messaging/mqadv/$(MQ_ARCHIVE_DEV)
|
||||
|
||||
downloads/$(MQ_SDK_ARCHIVE):
|
||||
$(info $(SPACER)$(shell printf $(TITLE)"Downloading IBM MQ Advanced redistributable client "$(MQ_VERSION)$(END)))
|
||||
mkdir -p downloads
|
||||
cd downloads; curl -LO https://public.dhe.ibm.com/ibmdl/export/pub/software/websphere/messaging/mqdev/redist/$(MQ_SDK_ARCHIVE)
|
||||
|
||||
.PHONY: downloads
|
||||
downloads: downloads/$(MQ_ARCHIVE_DEV) downloads/$(MQ_SDK_ARCHIVE)
|
||||
|
||||
# Vendor Go dependencies for the Docker tests
|
||||
test/docker/vendor:
|
||||
cd test/docker && dep ensure -vendor-only
|
||||
|
||||
|
||||
.PHONY: check-prereqs
|
||||
check-prereqs:
|
||||
$(info $(SPACER)$(shell printf $(TITLE)"Checking for prereqs"$(END)))
|
||||
which buildah || (echo "Missing required program buildah" && exit 1)
|
||||
which podman || (echo "Missing required program podman" && exit 1)
|
||||
yum list | grep yum-utils || (echo "Missing required package yum-utils" && exit 1)
|
||||
|
||||
.PHONY: check-test-prereqs
|
||||
check-test-prereqs:
|
||||
$(info $(SPACER)$(shell printf $(TITLE)"Checking for prereqs"$(END)))
|
||||
which buildah || (echo "Missing required program buildah" && exit 1)
|
||||
which docker || (echo "Missing required program docker" && exit 1)
|
||||
|
||||
.PHONY: test-advancedserver
|
||||
test-advancedserver: check-test-prereqs test/docker/vendor
|
||||
$(info $(SPACER)$(shell printf $(TITLE)"Test $(MQ_IMAGE_ADVANCEDSERVER) on $(shell docker --version)"$(END)))
|
||||
sudo buildah push $(MQ_IMAGE_ADVANCEDSERVER) docker-daemon:$(MQ_IMAGE_ADVANCEDSERVER)
|
||||
docker tag docker.io/$(MQ_IMAGE_ADVANCEDSERVER) $(MQ_IMAGE_ADVANCEDSERVER)
|
||||
cd test/docker && TEST_IMAGE=$(MQ_IMAGE_ADVANCEDSERVER) EXPECTED_LICENSE=Production go test $(TEST_OPTS_DOCKER)
|
||||
|
||||
|
||||
.PHONY: test-devserver
|
||||
test-devserver: check-test-prereqs test/docker/vendor
|
||||
$(info $(SPACER)$(shell printf $(TITLE)"Test $(MQ_IMAGE_DEVSERVER) on $(shell docker --version)"$(END)))
|
||||
sudo buildah push $(MQ_IMAGE_DEVSERVER) docker-daemon:$(MQ_IMAGE_DEVSERVER)
|
||||
docker tag docker.io/$(MQ_IMAGE_DEVSERVER) $(MQ_IMAGE_DEVSERVER)
|
||||
cd test/docker && TEST_IMAGE=$(MQ_IMAGE_DEVSERVER) EXPECTED_LICENSE=Developer DEV_JMS_IMAGE=$(DEV_JMS_IMAGE) go test -tags mqdev $(TEST_OPTS_DOCKER)
|
||||
|
||||
|
||||
.PHONY: build-advancedserver
|
||||
build-advancedserver: check-prereqs downloads/$(MQ_ARCHIVE) build-go-programs
|
||||
$(info $(SPACER)$(shell printf $(TITLE)"Build $(MQ_IMAGE_ADVANCEDSERVER)"$(END)))
|
||||
sudo mq-advanced-server-rhel/mq-buildah.sh "$(MQ_ARCHIVE)" "$(MQ_PACKAGES)" "$(MQ_IMAGE_ADVANCEDSERVER)" "$(MQ_VERSION)" "$(MQDEV)"
|
||||
|
||||
|
||||
.PHONY: build-devserver
|
||||
build-devserver: MQDEV=TRUE
|
||||
build-devserver: MQ_PACKAGES=MQSeriesRuntime-*.rpm MQSeriesServer-*.rpm MQSeriesJava*.rpm MQSeriesJRE*.rpm MQSeriesGSKit*.rpm MQSeriesMsg*.rpm MQSeriesSamples*.rpm MQSeriesAMS-*.rpm MQSeriesWeb-*.rpm
|
||||
build-devserver: check-prereqs downloads/$(MQ_ARCHIVE_DEV) build-go-programs
|
||||
$(info $(SPACER)$(shell printf $(TITLE)"Build $(MQ_IMAGE_DEVSERVER)"$(END)))
|
||||
sudo mq-advanced-server-rhel/mq-buildah.sh "$(MQ_ARCHIVE_DEV)" "$(MQ_PACKAGES)" "$(MQ_IMAGE_DEVSERVER_BASE)" "$(MQ_VERSION)" "$(MQDEV)"
|
||||
sudo mq-advanced-server-rhel/mqdev-buildah.sh "$(MQ_IMAGE_DEVSERVER_BASE)" "$(MQ_IMAGE_DEVSERVER)" "$(MQ_VERSION)"
|
||||
|
||||
|
||||
.PHONY: build-mqgolang-sdk
|
||||
build-mqgolang-sdk: check-prereqs downloads/$(MQ_SDK_ARCHIVE)
|
||||
$(info $(SPACER)$(shell printf $(TITLE)"Build mq-golang SDK"$(END)))
|
||||
sudo mq-advanced-server-rhel/mq-golang-sdk-buildah.sh "$(MQ_SDK_ARCHIVE)" "$(MQ_IMAGE_GOLANG_SDK)"
|
||||
|
||||
.PHONY: build-go-programs
|
||||
build-go-programs: check-prereqs downloads/$(MQ_SDK_ARCHIVE) build-mqgolang-sdk
|
||||
$(info $(SPACER)$(shell printf $(TITLE)"Build go programs"$(END)))
|
||||
IMAGE_REVISION=$(IMAGE_REVISION) IMAGE_SOURCE=$(IMAGE_SOURCE) sudo --preserve-env mq-advanced-server-rhel/go-buildah.sh "$(MQ_IMAGE_GOLANG_SDK)" "$(MQDEV)"
|
||||
|
||||
.PHONY: build-devjmstest
|
||||
build-devjmstest: check-test-prereqs
|
||||
$(info $(SPACER)$(shell printf $(TITLE)"Build JMS tests for developer config"$(END)))
|
||||
cd test/messaging && sudo ./buildah.sh $(DEV_JMS_IMAGE)
|
||||
sudo buildah push $(DEV_JMS_IMAGE) docker-daemon:$(DEV_JMS_IMAGE)
|
||||
docker tag docker.io/$(DEV_JMS_IMAGE) $(DEV_JMS_IMAGE)
|
||||
|
||||
.PHONY: debug-vars
|
||||
debug-vars:
|
||||
@echo MQ_VERSION=$(MQ_VERSION)
|
||||
@echo MQ_VERSION_VRM=$(MQ_VERSION_VRM)
|
||||
@echo MQ_ARCHIVE=$(MQ_ARCHIVE)
|
||||
@echo MQ_SDK_ARCHIVE=$(MQ_SDK_ARCHIVE)
|
||||
@echo MQ_IMAGE_GOLANG_SDK=$(MQ_IMAGE_GOLANG_SDK)
|
||||
@echo MQ_IMAGE_DEVSERVER_BASE=$(MQ_IMAGE_DEVSERVER_BASE)
|
||||
@echo MQ_IMAGE_DEVSERVER=$(MQ_IMAGE_DEVSERVER)
|
||||
@echo MQ_IMAGE_ADVANCEDSERVER=$(MQ_IMAGE_ADVANCEDSERVER)
|
||||
|
||||
include formatting.mk
|
||||
291
Makefile-UBUNTU
291
Makefile-UBUNTU
@@ -1,291 +0,0 @@
|
||||
# © Copyright IBM Corporation 2017, 2019
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
###############################################################################
|
||||
# Conditional variables - you can override the values of these variables from
|
||||
# the command line
|
||||
###############################################################################
|
||||
# BASE_IMAGE is the base image to use for MQ, for example "ubuntu" or "rhel"
|
||||
BASE_IMAGE ?= ubuntu:16.04
|
||||
# MQ_VERSION is the fully qualified MQ version number to build
|
||||
MQ_VERSION ?= 9.1.2.0
|
||||
# MQ_ARCHIVE is the name of the file, under the downloads directory, from which MQ Advanced can
|
||||
# be installed. The default value is derived from MQ_VERSION, BASE_IMAGE and architecture
|
||||
# Does not apply to MQ Advanced for Developers.
|
||||
MQ_ARCHIVE ?= IBM_MQ_$(MQ_VERSION_VRM)_$(MQ_ARCHIVE_TYPE)_$(MQ_ARCHIVE_ARCH).tar.gz
|
||||
# MQ_ARCHIVE_DEV is the name of the file, under the downloads directory, from which MQ Advanced
|
||||
# for Developers can be installed
|
||||
MQ_ARCHIVE_DEV ?= $(MQ_ARCHIVE_DEV_$(MQ_VERSION))
|
||||
# MQ_SDK_ARCHIVE specifies the archive to use for building the golang programs. Defaults vary on developer or advanced.
|
||||
MQ_SDK_ARCHIVE ?= $(MQ_ARCHIVE_DEV_$(MQ_VERSION))
|
||||
# Options to `go test` for the Docker tests
|
||||
TEST_OPTS_DOCKER ?=
|
||||
# MQ_IMAGE_ADVANCEDSERVER is the name and tag of the built MQ Advanced image
|
||||
MQ_IMAGE_ADVANCEDSERVER ?=mqadvanced-server:$(MQ_VERSION)-$(ARCH)-$(BASE_IMAGE_TAG)
|
||||
# MQ_IMAGE_DEVSERVER is the name and tag of the built MQ Advanced for Developers image
|
||||
MQ_IMAGE_DEVSERVER ?=mqadvanced-server-dev:$(MQ_VERSION)-$(ARCH)-$(BASE_IMAGE_TAG)
|
||||
# MQ_IMAGE_SDK is the name and tag of the built MQ Advanced for Developers SDK image
|
||||
MQ_IMAGE_SDK ?=mq-sdk:$(MQ_VERSION)-$(ARCH)-$(BASE_IMAGE_TAG)
|
||||
# MQ_IMAGE_GOLANG_SDK is the name and tag of the built MQ Advanced for Developers SDK image, plus Go tools
|
||||
MQ_IMAGE_GOLANG_SDK ?=mq-golang-sdk:$(MQ_VERSION)-$(ARCH)-$(BASE_IMAGE_TAG)
|
||||
# DOCKER is the Docker command to run
|
||||
DOCKER ?= docker
|
||||
# MQ_PACKAGES specifies the MQ packages (.deb or .rpm) to install. Defaults vary on base image.
|
||||
MQ_PACKAGES ?=
|
||||
|
||||
###############################################################################
|
||||
# Other variables
|
||||
###############################################################################
|
||||
# ARCH is the platform architecture (e.g. x86_64, ppc64le or s390x)
|
||||
ARCH = $(shell uname -m)
|
||||
# BUILD_SERVER_CONTAINER is the name of the web server container used at build time
|
||||
BUILD_SERVER_CONTAINER=build-server
|
||||
# NUM_CPU is the number of CPUs available to Docker. Used to control how many
|
||||
# test run in parallel
|
||||
NUM_CPU = $(or $(shell docker info --format "{{ .NCPU }}"),2)
|
||||
# BASE_IMAGE_TAG is a normalized version of BASE_IMAGE, suitable for use in a Docker tag
|
||||
BASE_IMAGE_TAG=$(subst /,-,$(subst :,-,$(BASE_IMAGE)))
|
||||
MQ_IMAGE_DEVSERVER_BASE=mqadvanced-server-dev-base:$(MQ_VERSION)-$(ARCH)-$(BASE_IMAGE_TAG)
|
||||
# Docker image name to use for JMS tests
|
||||
DEV_JMS_IMAGE=mq-dev-jms-test
|
||||
# Variables for versioning
|
||||
IMAGE_REVISION=$(shell git rev-parse HEAD)
|
||||
IMAGE_SOURCE=$(shell git config --get remote.origin.url)
|
||||
EMPTY:=
|
||||
SPACE:= $(EMPTY) $(EMPTY)
|
||||
# MQ_VERSION_VRM is MQ_VERSION with only the Version, Release and Modifier fields (no Fix field). e.g. 9.1.2 instead of 9.1.2.0
|
||||
MQ_VERSION_VRM=$(subst $(SPACE),.,$(wordlist 1,3,$(subst .,$(SPACE),$(MQ_VERSION))))
|
||||
|
||||
ifneq (,$(findstring Microsoft,$(shell uname -r)))
|
||||
DOWNLOADS_DIR=$(patsubst /mnt/c%,C:%,$(realpath ./downloads/))
|
||||
else
|
||||
DOWNLOADS_DIR=$(realpath ./downloads/)
|
||||
endif
|
||||
|
||||
# Try to figure out which archive to use from the BASE_IMAGE
|
||||
ifeq "$(findstring ubuntu,$(BASE_IMAGE))" "ubuntu"
|
||||
MQ_ARCHIVE_TYPE=UBUNTU
|
||||
MQ_ARCHIVE_DEV_PLATFORM=ubuntu
|
||||
MQM_UID=999
|
||||
else
|
||||
MQ_ARCHIVE_TYPE=LINUX
|
||||
MQ_ARCHIVE_DEV_PLATFORM=linux
|
||||
MQM_UID=888
|
||||
endif
|
||||
# Try to figure out which archive to use from the architecture
|
||||
ifeq "$(ARCH)" "x86_64"
|
||||
MQ_ARCHIVE_ARCH=X86-64
|
||||
MQ_DEV_ARCH=x86-64
|
||||
else ifeq "$(ARCH)" "ppc64le"
|
||||
MQ_ARCHIVE_ARCH=LE_POWER
|
||||
MQ_DEV_ARCH=ppcle
|
||||
else ifeq "$(ARCH)" "s390x"
|
||||
MQ_ARCHIVE_ARCH=SYSTEM_Z
|
||||
MQ_DEV_ARCH=s390x
|
||||
endif
|
||||
# Archive names for IBM MQ Advanced for Developers
|
||||
MQ_ARCHIVE_DEV_9.1.0.0=mqadv_dev910_$(MQ_ARCHIVE_DEV_PLATFORM)_$(MQ_DEV_ARCH).tar.gz
|
||||
MQ_ARCHIVE_DEV_9.1.1.0=mqadv_dev911_$(MQ_ARCHIVE_DEV_PLATFORM)_$(MQ_DEV_ARCH).tar.gz
|
||||
MQ_ARCHIVE_DEV_9.1.2.0=mqadv_dev912_$(MQ_ARCHIVE_DEV_PLATFORM)_$(MQ_DEV_ARCH).tar.gz
|
||||
|
||||
###############################################################################
|
||||
# Build targets
|
||||
###############################################################################
|
||||
.PHONY: vars
|
||||
vars:
|
||||
#ifeq "$(findstring ubuntu,$(BASE_IMAGE))","ubuntu"
|
||||
@echo $(MQ_ARCHIVE_ARCH)
|
||||
@echo $(MQ_ARCHIVE_TYPE)
|
||||
@echo $(MQ_ARCHIVE)
|
||||
|
||||
.PHONY: default
|
||||
default: build-devserver test
|
||||
|
||||
# Build all components (except incubating ones)
|
||||
.PHONY: all
|
||||
all: build-devserver build-advancedserver
|
||||
|
||||
.PHONY: test-all
|
||||
test-all: build-devjmstest test-devserver test-advancedserver
|
||||
|
||||
.PHONY: devserver
|
||||
devserver: build-devserver build-devjmstest test-devserver
|
||||
|
||||
# Build incubating components
|
||||
.PHONY: incubating
|
||||
incubating: build-explorer
|
||||
|
||||
downloads/$(MQ_ARCHIVE_DEV):
|
||||
$(info $(SPACER)$(shell printf $(TITLE)"Downloading IBM MQ Advanced for Developers "$(MQ_VERSION)$(END)))
|
||||
mkdir -p downloads
|
||||
cd downloads; curl -LO https://public.dhe.ibm.com/ibmdl/export/pub/software/websphere/messaging/mqadv/$(MQ_ARCHIVE_DEV)
|
||||
|
||||
downloads/$(MQ_SDK_ARCHIVE):
|
||||
$(info $(SPACER)$(shell printf $(TITLE)"Downloading IBM MQ Advanced for Developers "$(MQ_VERSION)$(END)))
|
||||
mkdir -p downloads
|
||||
cd downloads; curl -LO https://public.dhe.ibm.com/ibmdl/export/pub/software/websphere/messaging/mqadv/$(MQ_SDK_ARCHIVE)
|
||||
|
||||
.PHONY: downloads
|
||||
downloads: downloads/$(MQ_ARCHIVE_DEV) downloads/$(MQ_SDK_ARCHIVE)
|
||||
|
||||
# Vendor Go dependencies for the Docker tests
|
||||
test/docker/vendor:
|
||||
cd test/docker && dep ensure -vendor-only
|
||||
|
||||
# Shortcut to just run the unit tests
|
||||
.PHONY: test-unit
|
||||
test-unit:
|
||||
docker build --target builder --file Dockerfile-server .
|
||||
|
||||
.PHONY: test-advancedserver
|
||||
test-advancedserver: test/docker/vendor
|
||||
$(info $(SPACER)$(shell printf $(TITLE)"Test $(MQ_IMAGE_ADVANCEDSERVER) on $(shell docker --version)"$(END)))
|
||||
docker inspect $(MQ_IMAGE_ADVANCEDSERVER)
|
||||
cd test/docker && TEST_IMAGE=$(MQ_IMAGE_ADVANCEDSERVER) EXPECTED_LICENSE=Production go test -parallel $(NUM_CPU) $(TEST_OPTS_DOCKER)
|
||||
|
||||
.PHONY: build-devjmstest
|
||||
build-devjmstest:
|
||||
$(info $(SPACER)$(shell printf $(TITLE)"Build JMS tests for developer config"$(END)))
|
||||
cd test/messaging && docker build --tag $(DEV_JMS_IMAGE) .
|
||||
|
||||
.PHONY: test-devserver
|
||||
test-devserver: test/docker/vendor
|
||||
$(info $(SPACER)$(shell printf $(TITLE)"Test $(MQ_IMAGE_DEVSERVER) on $(shell docker --version)"$(END)))
|
||||
docker inspect $(MQ_IMAGE_DEVSERVER)
|
||||
cd test/docker && TEST_IMAGE=$(MQ_IMAGE_DEVSERVER) EXPECTED_LICENSE=Developer DEV_JMS_IMAGE=$(DEV_JMS_IMAGE) IBMJRE=true go test -parallel $(NUM_CPU) -tags mqdev $(TEST_OPTS_DOCKER)
|
||||
|
||||
coverage:
|
||||
mkdir coverage
|
||||
|
||||
.PHONY: test-advancedserver-cover
|
||||
test-advancedserver-cover: test/docker/vendor coverage
|
||||
$(info $(SPACER)$(shell printf $(TITLE)"Test $(MQ_IMAGE_ADVANCEDSERVER) with code coverage on $(shell docker --version)"$(END)))
|
||||
rm -f ./coverage/unit*.cov
|
||||
# Run unit tests with coverage, for each package under 'internal'
|
||||
go list -f '{{.Name}}' ./internal/... | xargs -I {} go test -cover -covermode count -coverprofile ./coverage/unit-{}.cov ./internal/{}
|
||||
# ls -1 ./cmd | xargs -I {} go test -cover -covermode count -coverprofile ./coverage/unit-{}.cov ./cmd/{}/...
|
||||
echo 'mode: count' > ./coverage/unit.cov
|
||||
tail -q -n +2 ./coverage/unit-*.cov >> ./coverage/unit.cov
|
||||
go tool cover -html=./coverage/unit.cov -o ./coverage/unit.html
|
||||
|
||||
rm -f ./test/docker/coverage/*.cov
|
||||
rm -f ./coverage/docker.*
|
||||
mkdir -p ./test/docker/coverage/
|
||||
cd test/docker && TEST_IMAGE=$(MQ_IMAGE_ADVANCEDSERVER)-cover TEST_COVER=true go test $(TEST_OPTS_DOCKER)
|
||||
echo 'mode: count' > ./coverage/docker.cov
|
||||
tail -q -n +2 ./test/docker/coverage/*.cov >> ./coverage/docker.cov
|
||||
go tool cover -html=./coverage/docker.cov -o ./coverage/docker.html
|
||||
|
||||
echo 'mode: count' > ./coverage/combined.cov
|
||||
tail -q -n +2 ./coverage/unit.cov ./coverage/docker.cov >> ./coverage/combined.cov
|
||||
go tool cover -html=./coverage/combined.cov -o ./coverage/combined.html
|
||||
|
||||
define docker-build-mq
|
||||
# Create a temporary network to use for the build
|
||||
$(DOCKER) network create build
|
||||
# Start a web server to host the MQ downloadable (tar.gz) file
|
||||
$(DOCKER) run \
|
||||
--rm \
|
||||
--name $(BUILD_SERVER_CONTAINER) \
|
||||
--network build \
|
||||
--network-alias build \
|
||||
--volume $(DOWNLOADS_DIR):/usr/share/nginx/html:ro \
|
||||
--detach \
|
||||
nginx:alpine
|
||||
# Build the new image
|
||||
$(DOCKER) build \
|
||||
--tag $1 \
|
||||
--file $2 \
|
||||
--network build \
|
||||
--build-arg MQ_URL=http://build:80/$3 \
|
||||
--build-arg BASE_IMAGE=$(BASE_IMAGE) \
|
||||
--build-arg BUILDER_IMAGE=$(MQ_IMAGE_GOLANG_SDK) \
|
||||
--build-arg IMAGE_REVISION="$(IMAGE_REVISION)" \
|
||||
--build-arg IMAGE_SOURCE="$(IMAGE_SOURCE)" \
|
||||
--build-arg IMAGE_TAG="$1" \
|
||||
--build-arg MQM_UID=$(MQM_UID) \
|
||||
--label IBM_PRODUCT_ID=$4 \
|
||||
--label IBM_PRODUCT_NAME=$5 \
|
||||
--label IBM_PRODUCT_VERSION=$6 \
|
||||
--build-arg MQ_PACKAGES="$(MQ_PACKAGES)" \
|
||||
. ; $(DOCKER) kill $(BUILD_SERVER_CONTAINER) && $(DOCKER) network rm build
|
||||
endef
|
||||
|
||||
DOCKER_SERVER_VERSION=$(shell docker version --format "{{ .Server.Version }}")
|
||||
DOCKER_CLIENT_VERSION=$(shell docker version --format "{{ .Client.Version }}")
|
||||
.PHONY: docker-version
|
||||
docker-version:
|
||||
@test "$(word 1,$(subst ., ,$(DOCKER_CLIENT_VERSION)))" -ge "17" || ("$(word 1,$(subst ., ,$(DOCKER_CLIENT_VERSION)))" -eq "17" && "$(word 2,$(subst ., ,$(DOCKER_CLIENT_VERSION)))" -ge "05") || (echo "Error: Docker client 17.05 or greater is required" && exit 1)
|
||||
@test "$(word 1,$(subst ., ,$(DOCKER_SERVER_VERSION)))" -ge "17" || ("$(word 1,$(subst ., ,$(DOCKER_SERVER_VERSION)))" -eq "17" && "$(word 2,$(subst ., ,$(DOCKER_CLIENT_VERSION)))" -ge "05") || (echo "Error: Docker server 17.05 or greater is required" && exit 1)
|
||||
|
||||
.PHONY: build-advancedserver
|
||||
build-advancedserver: MQ_SDK_ARCHIVE=$(MQ_ARCHIVE)
|
||||
build-advancedserver: downloads/$(MQ_ARCHIVE) docker-version build-golang-sdk-ex
|
||||
$(info $(SPACER)$(shell printf $(TITLE)"Build $(MQ_IMAGE_ADVANCEDSERVER)"$(END)))
|
||||
$(call docker-build-mq,$(MQ_IMAGE_ADVANCEDSERVER),Dockerfile-server,$(MQ_ARCHIVE),"4486e8c4cc9146fd9b3ce1f14a2dfc5b","IBM MQ Advanced",$(MQ_VERSION))
|
||||
|
||||
.PHONY: build-devserver
|
||||
# Target-specific variable to add web server into devserver image
|
||||
ifeq "$(findstring ubuntu,$(BASE_IMAGE))" "ubuntu"
|
||||
build-devserver: MQ_PACKAGES=ibmmq-server ibmmq-java ibmmq-jre ibmmq-gskit ibmmq-msg-.* ibmmq-samples ibmmq-ams ibmmq-web
|
||||
else
|
||||
build-devserver: MQ_PACKAGES=MQSeriesRuntime-*.rpm MQSeriesServer-*.rpm MQSeriesJava*.rpm MQSeriesJRE*.rpm MQSeriesGSKit*.rpm MQSeriesMsg*.rpm MQSeriesSamples*.rpm MQSeriesAMS-*.rpm MQSeriesWeb-*.rpm
|
||||
endif
|
||||
build-devserver: MQ_SDK_ARCHIVE=$(MQ_ARCHIVE_DEV)
|
||||
build-devserver: downloads/$(MQ_ARCHIVE_DEV) docker-version build-golang-sdk-ex
|
||||
$(info $(shell printf $(TITLE)"Build $(MQ_IMAGE_DEVSERVER_BASE)"$(END)))
|
||||
$(call docker-build-mq,$(MQ_IMAGE_DEVSERVER_BASE),Dockerfile-server,$(MQ_ARCHIVE_DEV),"98102d16795c4263ad9ca075190a2d4d","IBM MQ Advanced for Developers (Non-Warranted)",$(MQ_VERSION))
|
||||
$(DOCKER) build --tag $(MQ_IMAGE_DEVSERVER) --build-arg IMAGE_SOURCE="$(IMAGE_SOURCE)" --build-arg IMAGE_REVISION="$(IMAGE_REVISION)" --build-arg IMAGE_TAG="$(MQ_IMAGE_DEVSERVER)" --build-arg BASE_IMAGE=$(MQ_IMAGE_DEVSERVER_BASE) --build-arg BUILDER_IMAGE=$(MQ_IMAGE_GOLANG_SDK) --build-arg MQM_UID=$(MQM_UID) --file incubating/mqadvanced-server-dev/Dockerfile .
|
||||
|
||||
.PHONY: build-advancedserver-cover
|
||||
build-advancedserver-cover: docker-version
|
||||
$(DOCKER) build --build-arg BASE_IMAGE=$(MQ_IMAGE_ADVANCEDSERVER) -t $(MQ_IMAGE_ADVANCEDSERVER)-cover -f Dockerfile-server.cover .
|
||||
|
||||
.PHONY: build-explorer
|
||||
ifeq "$(findstring ubuntu,$(BASE_IMAGE))" "ubuntu"
|
||||
build-explorer: MQ_PACKAGES=ibmmq-explorer
|
||||
else
|
||||
build-explorer: MQ_PACKAGES=MQSeriesRuntime*.rpm MQSeriesJRE*.rpm MQSeriesExplorer*.rpm
|
||||
endif
|
||||
build-explorer: downloads/$(MQ_ARCHIVE_DEV) docker-pull
|
||||
$(call docker-build-mq,mq-explorer:latest-$(ARCH),incubating/mq-explorer/Dockerfile,$(MQ_ARCHIVE_DEV),"98102d16795c4263ad9ca075190a2d4d","IBM MQ Advanced for Developers (Non-Warranted)",$(MQ_VERSION))
|
||||
|
||||
.PHONY: build-sdk
|
||||
build-sdk: downloads/$(MQ_SDK_ARCHIVE) build-sdk-ex
|
||||
|
||||
.PHONY: build-sdk-ex
|
||||
ifeq "$(findstring ubuntu,$(BASE_IMAGE))" "ubuntu"
|
||||
build-sdk-ex: MQ_PACKAGES=ibmmq-sdk ibmmq-samples build-essential
|
||||
else
|
||||
build-sdk-ex: MQ_PACKAGES=MQSeriesRuntime-*.rpm MQSeriesSDK-*.rpm MQSeriesSamples*.rpm
|
||||
endif
|
||||
build-sdk-ex: docker-version docker-pull
|
||||
$(info $(shell printf $(TITLE)"Build $(MQ_IMAGE_SDK)"$(END)))
|
||||
$(call docker-build-mq,$(MQ_IMAGE_SDK),incubating/mq-sdk/Dockerfile,$(MQ_SDK_ARCHIVE),"98102d16795c4263ad9ca075190a2d4d","IBM MQ Advanced for Developers SDK (Non-Warranted)",$(MQ_VERSION))
|
||||
|
||||
.PHONY: build-golang-sdk
|
||||
build-golang-sdk: downloads/$(MQ_SDK_ARCHIVE) build-golang-sdk-ex
|
||||
|
||||
.PHONY: build-golang-sdk-ex
|
||||
build-golang-sdk-ex: docker-version build-sdk-ex
|
||||
$(info $(shell printf $(TITLE)"Build $(MQ_IMAGE_GOLANG_SDK)"$(END)))
|
||||
@echo hello
|
||||
$(DOCKER) build --build-arg BASE_IMAGE=$(MQ_IMAGE_SDK) -t $(MQ_IMAGE_GOLANG_SDK) -f incubating/mq-golang-sdk/Dockerfile .
|
||||
@echo goodbye
|
||||
|
||||
.PHONY: docker-pull
|
||||
docker-pull:
|
||||
$(DOCKER) pull $(BASE_IMAGE)
|
||||
|
||||
include formatting.mk
|
||||
12650
NOTICES.txt
12650
NOTICES.txt
File diff suppressed because it is too large
Load Diff
13
README.md
13
README.md
@@ -1,12 +1,11 @@
|
||||
# IBM MQ container
|
||||
|
||||
|
||||
[](https://travis-ci.org/ibm-messaging/mq-container)
|
||||
|
||||
**Note**: The `master` branch may be in an *unstable or even broken state* during development.
|
||||
To get a stable version, please use the correct [branch](https://github.com/ibm-messaging/mq-container/branches) for your MQ version, instead of the `master` branch.
|
||||
|
||||
<img src="https://raw.githubusercontent.com/IBM/charts/master/logo/ibm-mq-icon.svg?sanitize=true" width="100" alt="IBM MQ logo" />
|
||||
|
||||
## Overview
|
||||
|
||||
Run [IBM® MQ](http://www-03.ibm.com/software/products/en/ibm-mq) in a container.
|
||||
@@ -35,7 +34,7 @@ See the [default developer configuration docs](docs/developer-config.md) for the
|
||||
|
||||
### Kubernetes
|
||||
|
||||
If you want to use IBM MQ in [Kubernetes](https://kubernetes.io), you can find an example [Helm](https://helm.sh/) chart here: [IBM charts](https://github.com/IBM/charts). This can be used to run the container on a cluster, such as [IBM Cloud Private](https://www.ibm.com/cloud-computing/products/ibm-cloud-private/) or the [IBM Cloud Kubernetes Service](https://www.ibm.com/cloud/container-service).
|
||||
If you want to use IBM MQ on [Kubernetes](https://kubernetes.io), you can find an example [Helm](https://helm.sh/) chart here: [IBM MQ Sample Helm Chart](https://github.com/ibm-messaging/mq-helm). This can be used to run the container on a Kubernetes cluster, such as the [IBM Cloud Kubernetes Service](https://www.ibm.com/cloud/container-service).
|
||||
|
||||
## Issues and contributions
|
||||
|
||||
@@ -46,12 +45,12 @@ For issues relating specifically to the container image or Helm chart, please us
|
||||
The Dockerfiles and associated code and scripts are licensed under the [Apache License 2.0](http://www.apache.org/licenses/LICENSE-2.0.html).
|
||||
Licenses for the products installed within the images are as follows:
|
||||
|
||||
- [IBM MQ Advanced for Developers](http://www14.software.ibm.com/cgi-bin/weblap/lap.pl?la_formnum=Z125-3301-14&li_formnum=L-APIG-AVCJ4S) (International License Agreement for Non-Warranted Programs). This license may be viewed from an image using the `LICENSE=view` environment variable as described above or by following the link above.
|
||||
- [IBM MQ Advanced](http://www14.software.ibm.com/cgi-bin/weblap/lap.pl?la_formnum=Z125-3301-14&li_formnum=L-APIG-AZYF4X) (International Program License Agreement). This license may be viewed from an image using the `LICENSE=view` environment variable as described above or by following the link above.
|
||||
- License information for Ubuntu packages may be found in `/usr/share/doc/${package}/copyright`
|
||||
- [IBM MQ Advanced for Developers](http://www14.software.ibm.com/cgi-bin/weblap/lap.pl?la_formnum=Z125-3301-14&li_formnum=L-APIG-CAUEQC) (International License Agreement for Non-Warranted Programs). This license may be viewed from an image using the `LICENSE=view` environment variable as described above or by following the link above.
|
||||
- [IBM MQ Advanced](http://www14.software.ibm.com/cgi-bin/weblap/lap.pl?la_formnum=Z125-3301-14&li_formnum=L-APIG-CAUEBE) (International Program License Agreement). This license may be viewed from an image using the `LICENSE=view` environment variable as described above or by following the link above.
|
||||
|
||||
Note: The IBM MQ Advanced for Developers license does not permit further distribution and the terms restrict usage to a developer machine.
|
||||
|
||||
|
||||
## Copyright
|
||||
|
||||
© Copyright IBM Corporation 2015, 2018
|
||||
© Copyright IBM Corporation 2015, 2022
|
||||
|
||||
50
authservice/mqhtpass/Makefile
Normal file
50
authservice/mqhtpass/Makefile
Normal file
@@ -0,0 +1,50 @@
|
||||
# © Copyright IBM Corporation 2017, 2020
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# This Makefile expects the following to be installed:
|
||||
# - gcc
|
||||
# - ldd
|
||||
# - MQ SDK (mqm_r library, plus header files)
|
||||
# - Apache Portable Runtime (apr-1 and aprutil-1 libraries, plus header files)
|
||||
|
||||
SRC_DIR = src
|
||||
BUILD_DIR = ./build
|
||||
|
||||
# Flags passed to the C compiler. Need to use gnu11 to get POSIX functions needed for file locking.
|
||||
CFLAGS += -std=gnu11 -fPIC -Wall -m64
|
||||
|
||||
LIB_APR = -L/usr/lib64 -lapr-1 -laprutil-1
|
||||
LIB_MQ = -L/opt/mqm/lib64 -lmqm_r
|
||||
|
||||
all: $(BUILD_DIR)/mqhtpass.so $(BUILD_DIR)/htpass_test
|
||||
|
||||
$(BUILD_DIR)/log.o : $(SRC_DIR)/log.c $(SRC_DIR)/log.h
|
||||
mkdir -p ${dir $@}
|
||||
gcc $(CFLAGS) -c $(SRC_DIR)/log.c -o $@
|
||||
|
||||
$(BUILD_DIR)/htpass.o : $(SRC_DIR)/htpass.c $(SRC_DIR)/htpass.h
|
||||
mkdir -p ${dir $@}
|
||||
gcc $(CFLAGS) -c $(SRC_DIR)/htpass.c -I /usr/include/apr-1 -o $@
|
||||
|
||||
$(BUILD_DIR)/htpass_test : $(BUILD_DIR)/htpass.o $(BUILD_DIR)/log.o
|
||||
mkdir -p ${dir $@}
|
||||
gcc $(CFLAGS) $(LIB_APR) -lpthread $(SRC_DIR)/htpass_test.c $^ -o $@
|
||||
# Run HTPasswd tests, and print log if they fail
|
||||
$@ || (cat htpass_test*.log && exit 1)
|
||||
|
||||
$(BUILD_DIR)/mqhtpass.so : $(BUILD_DIR)/log.o $(BUILD_DIR)/htpass.o
|
||||
mkdir -p ${dir $@}
|
||||
# NOTE: rpath for libapr will be different on Ubuntu
|
||||
gcc $(CFLAGS) -I/opt/mqm/inc -D_REENTRANT $(LIB_APR) $(LIB_MQ) -Wl,-rpath,/opt/mqm/lib64 -Wl,-rpath,/usr/lib64 -shared $(SRC_DIR)/mqhtpass.c $^ -o $@
|
||||
ldd $@
|
||||
145
authservice/mqhtpass/src/htpass.c
Normal file
145
authservice/mqhtpass/src/htpass.c
Normal file
@@ -0,0 +1,145 @@
|
||||
/*
|
||||
© Copyright IBM Corporation 2021
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "log.h"
|
||||
#include "htpass.h"
|
||||
#include <linux/limits.h>
|
||||
#include <apr_general.h>
|
||||
#include <apr_errno.h>
|
||||
#include <apr_md5.h>
|
||||
|
||||
bool htpass_valid_file(char *filename)
|
||||
{
|
||||
bool valid = true;
|
||||
FILE *fp;
|
||||
char *huser;
|
||||
|
||||
fp = fopen(filename, "r");
|
||||
if (fp == NULL)
|
||||
{
|
||||
log_errorf("Error %d opening htpasswd file '%s'", errno, filename);
|
||||
}
|
||||
if (fp)
|
||||
{
|
||||
const size_t line_size = 1024;
|
||||
char *line = malloc(line_size);
|
||||
while (fgets(line, line_size, fp) != NULL)
|
||||
{
|
||||
char *saveptr;
|
||||
// Need to use strtok_r to be safe for multiple threads
|
||||
huser = strtok_r(line, ":", &saveptr);
|
||||
if (strlen(huser) >= 12)
|
||||
{
|
||||
log_errorf("Invalid htpasswd file for use with IBM MQ. User '%s' is longer than twelve characters", huser);
|
||||
valid = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
fclose(fp);
|
||||
if (line)
|
||||
{
|
||||
free(line);
|
||||
}
|
||||
}
|
||||
return valid;
|
||||
}
|
||||
|
||||
char *find_hash(char *filename, char *user)
|
||||
{
|
||||
bool found = false;
|
||||
FILE *fp;
|
||||
char *huser;
|
||||
char *hash;
|
||||
|
||||
fp = fopen(filename, "r");
|
||||
if (fp == NULL)
|
||||
{
|
||||
log_errorf("Error %d opening htpasswd file '%s'", errno, filename);
|
||||
}
|
||||
if (fp)
|
||||
{
|
||||
const size_t line_size = 1024;
|
||||
char *line = malloc(line_size);
|
||||
while (fgets(line, line_size, fp) != NULL)
|
||||
{
|
||||
char *saveptr;
|
||||
// Need to use strtok_r to be safe for multiple threads
|
||||
huser = strtok_r(line, ":", &saveptr);
|
||||
if (huser && (strcmp(user, huser) == 0))
|
||||
{
|
||||
// Make a duplicate of the string, because we'll be keeping it
|
||||
hash = strdup(strtok_r(NULL, " \r\n\t", &saveptr));
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
fclose(fp);
|
||||
if (line)
|
||||
{
|
||||
free(line);
|
||||
}
|
||||
}
|
||||
if (!found)
|
||||
{
|
||||
hash = NULL;
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
int htpass_authenticate_user(char *filename, char *user, char *password)
|
||||
{
|
||||
char *hash = find_hash(filename, user);
|
||||
int result = -1;
|
||||
if (hash == NULL)
|
||||
{
|
||||
result = HTPASS_INVALID_USER;
|
||||
log_debugf("User does not exist. user=%s", user);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Use the Apache Portable Runtime utilities to validate the password against the hash.
|
||||
// Supports multiple hashing algorithms, but we should only be using bcrypt
|
||||
apr_status_t status = apr_password_validate(password, hash);
|
||||
// status is usually either APR_SUCCESS or APR_EMISMATCH
|
||||
if (status == APR_SUCCESS)
|
||||
{
|
||||
result = HTPASS_VALID;
|
||||
log_debugf("Correct password supplied. user=%s", user);
|
||||
}
|
||||
else
|
||||
{
|
||||
result = HTPASS_INVALID_PASSWORD;
|
||||
log_debugf("Incorrect password supplied. user=%s", user);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool htpass_valid_user(char *filename, char *user)
|
||||
{
|
||||
char *hash = find_hash(filename, user);
|
||||
bool valid = false;
|
||||
if (hash != NULL)
|
||||
{
|
||||
valid = true;
|
||||
}
|
||||
return valid;
|
||||
}
|
||||
49
authservice/mqhtpass/src/htpass.h
Normal file
49
authservice/mqhtpass/src/htpass.h
Normal file
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
© Copyright IBM Corporation 2021
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef _HTPASS_H
|
||||
#define _HTPASS_H
|
||||
|
||||
#define HTPASS_VALID 0
|
||||
#define HTPASS_INVALID_USER 1
|
||||
#define HTPASS_INVALID_PASSWORD 2
|
||||
|
||||
/**
|
||||
* Validate an HTPasswd file for use with IBM MQ.
|
||||
*
|
||||
* @param filename the HTPasswd file
|
||||
*/
|
||||
_Bool htpass_valid_file(char *filename);
|
||||
|
||||
/**
|
||||
* Authenticate a user, based on the supplied file name.
|
||||
*
|
||||
* @param filename the HTPasswd file
|
||||
* @param user the user name to authenticate
|
||||
* @param password the password of the user
|
||||
* @return HTPASS_VALID, HTPASS_INVALID_USER or HTPASS_INVALID_PASSWORD
|
||||
*/
|
||||
int htpass_authenticate_user(char *filename, char *user, char *password);
|
||||
|
||||
/**
|
||||
* Validate that a user exists in the password file.
|
||||
*
|
||||
* @param filename the HTPasswd file
|
||||
* @param user the user name to validate
|
||||
*/
|
||||
_Bool htpass_valid_user(char *filename, char *user);
|
||||
|
||||
#endif
|
||||
223
authservice/mqhtpass/src/htpass_test.c
Normal file
223
authservice/mqhtpass/src/htpass_test.c
Normal file
@@ -0,0 +1,223 @@
|
||||
/*
|
||||
© Copyright IBM Corporation 2021
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include "log.h"
|
||||
#include "htpass.h"
|
||||
|
||||
// Headers for multi-threaded tests
|
||||
#include <pthread.h>
|
||||
|
||||
// Start a test and log the function name
|
||||
#define test_start() printf("=== RUN: %s\n", __func__)
|
||||
|
||||
// Indicate test has passed
|
||||
#define test_pass() printf("--- PASS: %s\n", __func__)
|
||||
|
||||
// Indicate test has failed
|
||||
void test_fail(const char *test_name)
|
||||
{
|
||||
printf("--- FAIL: %s\n", test_name);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Simple tests for file validation
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
void test_htpass_valid_file_ok()
|
||||
{
|
||||
test_start();
|
||||
int ok = htpass_valid_file("./src/htpass_test.htpasswd");
|
||||
if (!ok)
|
||||
test_fail(__func__);
|
||||
test_pass();
|
||||
}
|
||||
|
||||
void test_htpass_valid_file_too_long()
|
||||
{
|
||||
test_start();
|
||||
int ok = htpass_valid_file("./src/htpass_test_invalid.htpasswd");
|
||||
if (ok)
|
||||
test_fail(__func__);
|
||||
test_pass();
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Simple tests for authentication
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
void test_htpass_authenticate_user_fred_valid()
|
||||
{
|
||||
test_start();
|
||||
int rc = htpass_authenticate_user("./src/htpass_test.htpasswd", "fred", "passw0rd");
|
||||
printf("%s: fred - %d\n", __func__, rc);
|
||||
if (rc != HTPASS_VALID)
|
||||
test_fail(__func__);
|
||||
test_pass();
|
||||
}
|
||||
|
||||
void test_htpass_authenticate_user_fred_invalid1()
|
||||
{
|
||||
test_start();
|
||||
int rc = htpass_authenticate_user("./src/htpass_test.htpasswd", "fred", "passw0rd ");
|
||||
printf("%s: fred - %d\n", __func__, rc);
|
||||
if (rc != HTPASS_INVALID_PASSWORD)
|
||||
test_fail(__func__);
|
||||
test_pass();
|
||||
}
|
||||
|
||||
void test_htpass_authenticate_user_fred_invalid2()
|
||||
{
|
||||
test_start();
|
||||
int rc = htpass_authenticate_user("./src/htpass_test.htpasswd", "fred", "");
|
||||
printf("%s: fred - %d\n", __func__, rc);
|
||||
if (rc != HTPASS_INVALID_PASSWORD)
|
||||
test_fail(__func__);
|
||||
test_pass();
|
||||
}
|
||||
|
||||
void test_htpass_authenticate_user_fred_invalid3()
|
||||
{
|
||||
test_start();
|
||||
int rc = htpass_authenticate_user("./src/htpass_test.htpasswd", "fred", "clearlywrong");
|
||||
printf("%s: fred - %d\n", __func__, rc);
|
||||
if (rc != HTPASS_INVALID_PASSWORD)
|
||||
test_fail(__func__);
|
||||
test_pass();
|
||||
}
|
||||
|
||||
void test_htpass_authenticate_user_barney_valid()
|
||||
{
|
||||
test_start();
|
||||
int rc = htpass_authenticate_user("./src/htpass_test.htpasswd", "barney", "s3cret");
|
||||
printf("%s: barney - %d\n", __func__, rc);
|
||||
if (rc != HTPASS_VALID)
|
||||
test_fail(__func__);
|
||||
test_pass();
|
||||
}
|
||||
|
||||
void test_htpass_authenticate_user_unknown()
|
||||
{
|
||||
test_start();
|
||||
int rc = htpass_authenticate_user("./src/htpass_test.htpasswd", "george", "s3cret");
|
||||
printf("%s: barney - %d\n", __func__, rc);
|
||||
if (rc != HTPASS_INVALID_USER)
|
||||
test_fail(__func__);
|
||||
test_pass();
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Multi-threaded test
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
#define NUM_THREADS 5
|
||||
// Number of tests to perform per thread. Higher numbers are more likely to trigger timing issue.
|
||||
#define NUM_TESTS_PER_THREAD 1000
|
||||
// Maximum number of JSON errors to report (log can get flooded)
|
||||
#define MAX_JSON_ERRORS 10
|
||||
|
||||
// Authenticate multiple users, multiple times
|
||||
void *authenticate_many_times(void *p)
|
||||
{
|
||||
for (int i = 0; i < NUM_TESTS_PER_THREAD; i++)
|
||||
{
|
||||
int rc = htpass_authenticate_user("./src/htpass_test.htpasswd", "barney", "s3cret");
|
||||
if (rc != HTPASS_VALID)
|
||||
test_fail(__func__);
|
||||
rc = htpass_authenticate_user("./src/htpass_test.htpasswd", "fred", "passw0rd");
|
||||
if (rc != HTPASS_VALID)
|
||||
test_fail(__func__);
|
||||
}
|
||||
pthread_exit(NULL);
|
||||
}
|
||||
|
||||
void check_log_file_valid(char *filename)
|
||||
{
|
||||
int errors = 0;
|
||||
printf("--- Checking log file is valid\n");
|
||||
// Check that the JSON log file isn't corrupted
|
||||
FILE *log = fopen(filename, "r");
|
||||
if (log == NULL)
|
||||
{
|
||||
test_fail(__func__);
|
||||
}
|
||||
const size_t line_size = 1024;
|
||||
char *line = malloc(line_size);
|
||||
while (fgets(line, line_size, log) != NULL)
|
||||
{
|
||||
if ((line[0] != '{') && (errors < MAX_JSON_ERRORS))
|
||||
{
|
||||
printf("*** Invalid JSON detected: %s\n", line);
|
||||
errors++;
|
||||
}
|
||||
}
|
||||
if (line)
|
||||
{
|
||||
free(line);
|
||||
}
|
||||
fclose(log);
|
||||
}
|
||||
|
||||
// Test authenticate_user with multiple threads, each doing many authentications
|
||||
void test_htpass_authenticate_user_multithreaded(char *logfile)
|
||||
{
|
||||
pthread_t threads[NUM_THREADS];
|
||||
int rc;
|
||||
test_start();
|
||||
// Re-initialize the log to use a file for the multi-threaded test
|
||||
log_init(logfile);
|
||||
for (int i = 0; i < NUM_THREADS; i++)
|
||||
{
|
||||
printf("Creating thread %d\n", i);
|
||||
rc = pthread_create(&threads[i], NULL, authenticate_many_times, NULL);
|
||||
if (rc)
|
||||
{
|
||||
printf("Error: Unable to create thread, %d\n", rc);
|
||||
test_fail(__func__);
|
||||
}
|
||||
}
|
||||
// Wait for all the threads to complete
|
||||
for (int i = 0; i < NUM_THREADS; i++)
|
||||
{
|
||||
pthread_join(threads[i], NULL);
|
||||
}
|
||||
check_log_file_valid(logfile);
|
||||
test_pass();
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
int main()
|
||||
{
|
||||
// Turn on debugging for the tests
|
||||
setenv("DEBUG", "true", true);
|
||||
log_init("htpass_test.log");
|
||||
test_htpass_valid_file_ok();
|
||||
test_htpass_valid_file_too_long();
|
||||
test_htpass_authenticate_user_fred_valid();
|
||||
test_htpass_authenticate_user_fred_invalid1();
|
||||
test_htpass_authenticate_user_fred_invalid2();
|
||||
test_htpass_authenticate_user_fred_invalid3();
|
||||
test_htpass_authenticate_user_barney_valid();
|
||||
test_htpass_authenticate_user_unknown();
|
||||
log_close();
|
||||
|
||||
// Call multi-threaded test last, because it re-initializes the log to use a file
|
||||
test_htpass_authenticate_user_multithreaded("htpass_test_multithreaded.log");
|
||||
}
|
||||
2
authservice/mqhtpass/src/htpass_test.htpasswd
Normal file
2
authservice/mqhtpass/src/htpass_test.htpasswd
Normal file
@@ -0,0 +1,2 @@
|
||||
fred:$2y$05$3Fp9epsqEwWOHdyj9Ngf9.qfX34kzc9zNrdQ7kac0GmcCvQjIkAwy
|
||||
barney:$2y$05$l8EoyCQ9y2PyfUzIDDfTyu7SSaJEYB1TuHy07xZvN7xt/pR3SIw0a
|
||||
3
authservice/mqhtpass/src/htpass_test_invalid.htpasswd
Normal file
3
authservice/mqhtpass/src/htpass_test_invalid.htpasswd
Normal file
@@ -0,0 +1,3 @@
|
||||
fred:$2y$05$3Fp9epsqEwWOHdyj9Ngf9.qfX34kzc9zNrdQ7kac0GmcCvQjIkAwy
|
||||
barney:$2y$05$l8EoyCQ9y2PyfUzIDDfTyu7SSaJEYB1TuHy07xZvN7xt/pR3SIw0a
|
||||
namewhichisfartoolongformq:$2y$05$l8EoyCQ9y2PyfUzIDDfTyu7SSaJEYB1TuHy07xZvN7xt/pR3SIw0a
|
||||
152
authservice/mqhtpass/src/log.c
Normal file
152
authservice/mqhtpass/src/log.c
Normal file
@@ -0,0 +1,152 @@
|
||||
/*
|
||||
© Copyright IBM Corporation 2021
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <sys/time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
FILE *fp = NULL;
|
||||
int pid;
|
||||
char hostname[255];
|
||||
bool debug = false;
|
||||
|
||||
/**
|
||||
* Determine whether debugging is enabled or not, using an environment variable.
|
||||
*/
|
||||
void init_debug(){
|
||||
char *debug_env = getenv("DEBUG");
|
||||
if (debug_env != NULL)
|
||||
{
|
||||
// Enable debug logging if the DEBUG environment variable is set
|
||||
if (strncmp(debug_env, "true", 4) || strncmp(debug_env, "1", 1))
|
||||
{
|
||||
debug = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal function to initialize the log with the given file mode.
|
||||
*/
|
||||
int log_init_internal(char *filename, const char *mode)
|
||||
{
|
||||
int result = 0;
|
||||
pid = getpid();
|
||||
hostname[254] = '\0';
|
||||
gethostname(hostname, 254);
|
||||
if (!fp)
|
||||
{
|
||||
fp = fopen(filename, "a");
|
||||
if (fp)
|
||||
{
|
||||
// Disable buffering for this file
|
||||
setbuf(fp, NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
result = 1;
|
||||
}
|
||||
}
|
||||
init_debug();
|
||||
return result;
|
||||
}
|
||||
|
||||
int log_init_reset(char *filename)
|
||||
{
|
||||
// Open the log file for writing (overwrite if it already exists)
|
||||
return log_init_internal(filename, "w");
|
||||
}
|
||||
|
||||
int log_init(char *filename)
|
||||
{
|
||||
// Open the log file file for appending
|
||||
return log_init_internal(filename, "a");
|
||||
}
|
||||
|
||||
void log_init_file(FILE *f)
|
||||
{
|
||||
fp = f;
|
||||
init_debug();
|
||||
}
|
||||
|
||||
void log_close()
|
||||
{
|
||||
if (fp)
|
||||
{
|
||||
fclose(fp);
|
||||
fp = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void log_printf(const char *source_file, int source_line, const char *level, const char *format, ...)
|
||||
{
|
||||
if (fp)
|
||||
{
|
||||
// If this is a DEBUG message, and debugging is off
|
||||
if ((strncmp(level, "DEBUG", 5) == 0) && !debug)
|
||||
{
|
||||
return;
|
||||
}
|
||||
char buf[1024] = "";
|
||||
char *cur = buf;
|
||||
char* const end = buf + sizeof buf;
|
||||
char date_buf[70];
|
||||
struct tm *utc;
|
||||
time_t t;
|
||||
struct timeval now;
|
||||
|
||||
gettimeofday(&now, NULL);
|
||||
t = now.tv_sec;
|
||||
t = time(NULL);
|
||||
utc = gmtime(&t);
|
||||
|
||||
cur += snprintf(cur, end-cur, "{");
|
||||
cur += snprintf(cur, end-cur, "\"loglevel\":\"%s\"", level);
|
||||
// Print ISO-8601 time and date
|
||||
if (strftime(date_buf, sizeof date_buf, "%FT%T", utc))
|
||||
{
|
||||
// Round microseconds down to milliseconds, for consistency
|
||||
cur += snprintf(cur, end-cur, ", \"ibm_datetime\":\"%s.%03ldZ\"", date_buf, now.tv_usec / 1000);
|
||||
}
|
||||
cur += snprintf(cur, end-cur, ", \"ibm_processId\":\"%d\"", pid);
|
||||
cur += snprintf(cur, end-cur, ", \"host\":\"%s\"", hostname);
|
||||
cur += snprintf(cur, end-cur, ", \"module\":\"%s:%d\"", source_file, source_line);
|
||||
cur += snprintf(cur, end-cur, ", \"message\":\"");
|
||||
|
||||
if (strncmp(level, "DEBUG", 5) == 0)
|
||||
{
|
||||
// Add a prefix on any debug messages
|
||||
cur += snprintf(cur, end-cur, "mqhtpass: ");
|
||||
}
|
||||
|
||||
// Print log message, using varargs
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
cur += vsnprintf(cur, end-cur, format, args);
|
||||
va_end(args);
|
||||
cur += snprintf(cur, end-cur, "\"}\n");
|
||||
|
||||
// Important: Just do one file write, to prevent problems with multi-threading.
|
||||
// This only works if the log message is not too long for the buffer.
|
||||
fprintf(fp, buf);
|
||||
}
|
||||
}
|
||||
|
||||
63
authservice/mqhtpass/src/log.h
Normal file
63
authservice/mqhtpass/src/log.h
Normal file
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
© Copyright IBM Corporation 2021
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef _LOG_H
|
||||
#define _LOG_H
|
||||
|
||||
/**
|
||||
* Initialize the log to use the given file name, wiping any existing contents.
|
||||
*/
|
||||
int log_init_reset(char *filename);
|
||||
|
||||
/**
|
||||
* Initialize the log to use the given file name.
|
||||
*/
|
||||
int log_init(char *filename);
|
||||
|
||||
/**
|
||||
* Initialize the log with an existing file handle.
|
||||
*/
|
||||
void log_init_file(FILE *f);
|
||||
|
||||
/**
|
||||
* Write a message to the log file, based on a printf format string.
|
||||
*
|
||||
* @param source_file the name of the source code file submitting this log message
|
||||
* @param source_line the line of code in the source file
|
||||
* @param level the log level, one of "DEBUG", "INFO" or "ERROR"
|
||||
* @param format the printf format string for the message
|
||||
*/
|
||||
void log_printf(const char *source_file, int source_line, const char *level, const char *format, ...);
|
||||
|
||||
void log_close();
|
||||
|
||||
/**
|
||||
* Variadic macro to write an informational message to the log file, based on a printf format string.
|
||||
*/
|
||||
#define log_infof(format,...) log_printf(__FILE__, __LINE__, "INFO", format, ##__VA_ARGS__)
|
||||
|
||||
/**
|
||||
* Variadic macro to write an error message to the log file, based on a printf format string.
|
||||
*/
|
||||
#define log_errorf(format,...) log_printf(__FILE__, __LINE__, "ERROR", format, ##__VA_ARGS__)
|
||||
|
||||
/**
|
||||
* Variadic macro to write a debug message to the log file, based on a printf format string.
|
||||
*/
|
||||
#define log_debugf(format,...) log_printf(__FILE__, __LINE__, "DEBUG", format, ##__VA_ARGS__)
|
||||
|
||||
|
||||
#endif
|
||||
350
authservice/mqhtpass/src/mqhtpass.c
Normal file
350
authservice/mqhtpass/src/mqhtpass.c
Normal file
@@ -0,0 +1,350 @@
|
||||
/*
|
||||
© Copyright IBM Corporation 2021, 2022
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// This is a developer only configuration and not recommended for production usage.
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <cmqec.h>
|
||||
#include "log.h"
|
||||
#include "htpass.h"
|
||||
|
||||
// Declare the internal functions that implement the interface
|
||||
MQZ_INIT_AUTHORITY MQStart;
|
||||
static MQZ_AUTHENTICATE_USER mqhtpass_authenticate_user;
|
||||
static MQZ_FREE_USER mqhtpass_free_user;
|
||||
static MQZ_TERM_AUTHORITY mqhtpass_terminate;
|
||||
|
||||
#define LOG_FILE "/var/mqm/errors/mqhtpass.json"
|
||||
#define HTPASSWD_FILE "/etc/mqm/mq.htpasswd"
|
||||
#define NAME "MQ Advanced for Developers custom authentication service"
|
||||
|
||||
static char *trim(char *s);
|
||||
|
||||
/**
|
||||
* Initialization and entrypoint for the dynamically loaded
|
||||
* authorization installable service. It registers the addresses of the
|
||||
* other functions which are to be called by the queue manager.
|
||||
*
|
||||
* This function is called whenever the module is loaded. The Options
|
||||
* field will show whether it's a PRIMARY (i.e. during qmgr startup) or
|
||||
* SECONDARY.
|
||||
*/
|
||||
void MQENTRY MQStart(
|
||||
MQHCONFIG hc,
|
||||
MQLONG Options,
|
||||
MQCHAR48 QMgrName,
|
||||
MQLONG ComponentDataLength,
|
||||
PMQBYTE ComponentData,
|
||||
PMQLONG Version,
|
||||
PMQLONG pCompCode,
|
||||
PMQLONG pReason)
|
||||
{
|
||||
MQLONG CC = MQCC_OK;
|
||||
MQLONG Reason = MQRC_NONE;
|
||||
int log_rc = 0;
|
||||
|
||||
if (Options == MQZIO_PRIMARY)
|
||||
{
|
||||
// Reset the log file. The file could still get large if debug is turned on,
|
||||
// but this is a simpler solution for now.
|
||||
log_rc = log_init_reset(LOG_FILE);
|
||||
}
|
||||
else
|
||||
{
|
||||
log_rc = log_init(LOG_FILE);
|
||||
}
|
||||
|
||||
if (log_rc != 0)
|
||||
{
|
||||
CC = MQCC_FAILED;
|
||||
Reason = MQRC_INITIALIZATION_FAILED;
|
||||
}
|
||||
|
||||
if (Options == MQZIO_PRIMARY)
|
||||
{
|
||||
log_infof("Initializing %s", NAME);
|
||||
}
|
||||
log_debugf("MQStart options=%s qmgr=%s", ((Options == MQZIO_SECONDARY) ? "Secondary" : "Primary"), trim(QMgrName));
|
||||
|
||||
if (!htpass_valid_file(HTPASSWD_FILE))
|
||||
{
|
||||
CC = MQCC_FAILED;
|
||||
Reason = MQRC_INITIALIZATION_FAILED;
|
||||
}
|
||||
|
||||
// Initialize the functions to use for each entry point
|
||||
if (CC == MQCC_OK)
|
||||
{
|
||||
hc->MQZEP_Call(hc, MQZID_INIT_AUTHORITY, (PMQFUNC)MQStart, &CC, &Reason);
|
||||
}
|
||||
if (CC == MQCC_OK)
|
||||
{
|
||||
hc->MQZEP_Call(hc, MQZID_TERM_AUTHORITY, (PMQFUNC)mqhtpass_terminate, &CC, &Reason);
|
||||
}
|
||||
if (CC == MQCC_OK)
|
||||
{
|
||||
hc->MQZEP_Call(hc, MQZID_AUTHENTICATE_USER, (PMQFUNC)mqhtpass_authenticate_user, &CC, &Reason);
|
||||
}
|
||||
if (CC == MQCC_OK)
|
||||
{
|
||||
hc->MQZEP_Call(hc, MQZID_FREE_USER, (PMQFUNC)mqhtpass_free_user, &CC, &Reason);
|
||||
}
|
||||
*Version = MQZAS_VERSION_5;
|
||||
*pCompCode = CC;
|
||||
*pReason = Reason;
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called during the connection of any application which supplies an MQCSP (Connection Security Parameters).
|
||||
* This is the usual case.
|
||||
* See https://www.ibm.com/support/knowledgecenter/SSFKSJ_latest/com.ibm.mq.ref.dev.doc/q095610_.html
|
||||
*/
|
||||
static void MQENTRY mqhtpass_authenticate_user_csp(
|
||||
PMQCHAR pQMgrName,
|
||||
PMQCSP pSecurityParms,
|
||||
PMQZAC pApplicationContext,
|
||||
PMQZIC pIdentityContext,
|
||||
PMQPTR pCorrelationPtr,
|
||||
PMQBYTE pComponentData,
|
||||
PMQLONG pContinuation,
|
||||
PMQLONG pCompCode,
|
||||
PMQLONG pReason)
|
||||
{
|
||||
char *csp_user = NULL;
|
||||
char *csp_pass = NULL;
|
||||
|
||||
// Firstly, create null-terminated strings from the user credentials in the MQ CSP object
|
||||
csp_user = malloc(pSecurityParms->CSPUserIdLength + 1);
|
||||
if (!csp_user)
|
||||
{
|
||||
log_errorf("%s is unable to allocate memory for a user", NAME);
|
||||
*pCompCode = MQCC_FAILED;
|
||||
*pReason = MQRC_SERVICE_ERROR;
|
||||
return;
|
||||
}
|
||||
strncpy(csp_user, pSecurityParms->CSPUserIdPtr, pSecurityParms->CSPUserIdLength);
|
||||
csp_user[pSecurityParms->CSPUserIdLength] = 0;
|
||||
csp_pass = malloc((pSecurityParms->CSPPasswordLength + 1));
|
||||
if (!csp_pass)
|
||||
{
|
||||
log_errorf("%s is unable to allocate memory for a password", NAME);
|
||||
*pCompCode = MQCC_FAILED;
|
||||
*pReason = MQRC_SERVICE_ERROR;
|
||||
if (csp_user)
|
||||
{
|
||||
free(csp_user);
|
||||
}
|
||||
return;
|
||||
}
|
||||
strncpy(csp_pass, pSecurityParms->CSPPasswordPtr, pSecurityParms->CSPPasswordLength);
|
||||
csp_pass[pSecurityParms->CSPPasswordLength] = 0;
|
||||
log_debugf("%s with CSP user set. user=%s", __func__, csp_user);
|
||||
int auth_result = htpass_authenticate_user(HTPASSWD_FILE, csp_user, csp_pass);
|
||||
|
||||
if (auth_result == HTPASS_VALID)
|
||||
{
|
||||
// An OK completion code means MQ will accept this user is authenticated
|
||||
*pCompCode = MQCC_OK;
|
||||
*pReason = MQRC_NONE;
|
||||
// Tell the queue manager to stop trying other authorization services.
|
||||
*pContinuation = MQZCI_STOP;
|
||||
memcpy(pIdentityContext->UserIdentifier, csp_user, sizeof(pIdentityContext->UserIdentifier));
|
||||
log_debugf("Authenticated user=%s", pIdentityContext->UserIdentifier);
|
||||
}
|
||||
// If the htpasswd file does not have an entry for this user
|
||||
else if (auth_result == HTPASS_INVALID_USER)
|
||||
{
|
||||
*pCompCode = MQCC_WARNING;
|
||||
*pReason = MQRC_NONE;
|
||||
// Tell the queue manager to continue trying other authorization services, as they might have the user.
|
||||
*pContinuation = MQZCI_CONTINUE;
|
||||
log_debugf(
|
||||
"User authentication failed due to invalid user. user=%s effuser=%s applname=%s csp_user=%s cc=%d reason=%d",
|
||||
trim(pIdentityContext->UserIdentifier),
|
||||
trim(pApplicationContext->EffectiveUserID),
|
||||
trim(pApplicationContext->ApplName),
|
||||
trim(csp_user),
|
||||
*pCompCode,
|
||||
*pReason);
|
||||
}
|
||||
// If the htpasswd file has an entry for this user, but the password supplied is incorrect
|
||||
else if (auth_result == HTPASS_INVALID_PASSWORD)
|
||||
{
|
||||
*pCompCode = MQCC_WARNING;
|
||||
*pReason = MQRC_NOT_AUTHORIZED;
|
||||
// Tell the queue manager to stop trying other authorization services.
|
||||
*pContinuation = MQZCI_STOP;
|
||||
log_debugf(
|
||||
"User authentication failed due to invalid password. user=%s effuser=%s applname=%s csp_user=%s cc=%d reason=%d",
|
||||
trim(pIdentityContext->UserIdentifier),
|
||||
trim(pApplicationContext->EffectiveUserID),
|
||||
trim(pApplicationContext->ApplName),
|
||||
trim(csp_user),
|
||||
*pCompCode,
|
||||
*pReason);
|
||||
}
|
||||
if (csp_user)
|
||||
{
|
||||
free(csp_user);
|
||||
}
|
||||
if (csp_pass)
|
||||
{
|
||||
free(csp_pass);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called during the connection of any application.
|
||||
* For more information on the parameters, see https://www.ibm.com/support/knowledgecenter/en/SSFKSJ_latest/com.ibm.mq.ref.dev.doc/q110090_.html
|
||||
*/
|
||||
static void MQENTRY mqhtpass_authenticate_user(
|
||||
PMQCHAR pQMgrName,
|
||||
PMQCSP pSecurityParms,
|
||||
PMQZAC pApplicationContext,
|
||||
PMQZIC pIdentityContext,
|
||||
PMQPTR pCorrelationPtr,
|
||||
PMQBYTE pComponentData,
|
||||
PMQLONG pContinuation,
|
||||
PMQLONG pCompCode,
|
||||
PMQLONG pReason)
|
||||
{
|
||||
char *spuser = NULL;
|
||||
// By default, return a warning, which indicates to MQ that this
|
||||
// authorization service hasn't authenticated the user.
|
||||
*pCompCode = MQCC_WARNING;
|
||||
*pReason = MQRC_NONE;
|
||||
// By default, tell the queue manager to continue trying other
|
||||
// authorization services.
|
||||
*pContinuation = MQZCI_CONTINUE;
|
||||
|
||||
if ((pSecurityParms->AuthenticationType) == MQCSP_AUTH_USER_ID_AND_PWD)
|
||||
{
|
||||
mqhtpass_authenticate_user_csp(pQMgrName, pSecurityParms, pApplicationContext, pIdentityContext, pCorrelationPtr, pComponentData, pContinuation, pCompCode, pReason);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Password not supplied, so just check that the user ID is valid
|
||||
spuser = malloc(sizeof(PMQCHAR12) + 1);
|
||||
if (!spuser)
|
||||
{
|
||||
log_errorf("%s is unable to allocate memory to check a user", NAME);
|
||||
*pCompCode = MQCC_FAILED;
|
||||
*pReason = MQRC_SERVICE_ERROR;
|
||||
return;
|
||||
}
|
||||
strncpy(spuser, pApplicationContext->EffectiveUserID, strlen(pApplicationContext->EffectiveUserID));
|
||||
spuser[sizeof(PMQCHAR12)] = 0;
|
||||
log_debugf("%s without CSP user set. effectiveuid=%s env=%d, callertype=%d, type=%d, accttoken=%d applidentitydata=%d", __func__, spuser, pApplicationContext->Environment, pApplicationContext->CallerType, pApplicationContext->AuthenticationType, pIdentityContext->AccountingToken, pIdentityContext->ApplIdentityData);
|
||||
if (strncmp(spuser, "mqm", 3) == 0)
|
||||
{
|
||||
// Special case: pass the "mqm" user on for validation up the chain
|
||||
// A warning in the completion code means MQ will pass this to other authorization services
|
||||
*pCompCode = MQCC_WARNING;
|
||||
*pReason = MQRC_NONE;
|
||||
*pContinuation = MQZCI_CONTINUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
bool valid_user = htpass_valid_user(HTPASSWD_FILE, spuser);
|
||||
if (valid_user)
|
||||
{
|
||||
// An OK completion code means MQ will accept this user is authenticated
|
||||
*pCompCode = MQCC_OK;
|
||||
*pReason = MQRC_NONE;
|
||||
*pContinuation = MQZCI_STOP;
|
||||
memcpy(pIdentityContext->UserIdentifier, spuser, sizeof(pIdentityContext->UserIdentifier));
|
||||
}
|
||||
else
|
||||
{
|
||||
log_debugf(
|
||||
"User authentication failed user=%s effuser=%s applname=%s cspuser=%s cc=%d reason=%d",
|
||||
trim(pIdentityContext->UserIdentifier),
|
||||
trim(pApplicationContext->EffectiveUserID),
|
||||
trim(pApplicationContext->ApplName),
|
||||
trim(spuser),
|
||||
*pCompCode,
|
||||
*pReason);
|
||||
}
|
||||
if (spuser)
|
||||
{
|
||||
free(spuser);
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called during MQDISC, as the inverse of the call to authenticate.
|
||||
*/
|
||||
static void MQENTRY mqhtpass_free_user(
|
||||
PMQCHAR pQMgrName,
|
||||
PMQZFP pFreeParms,
|
||||
PMQBYTE pComponentData,
|
||||
PMQLONG pContinuation,
|
||||
|
||||
PMQLONG pCompCode,
|
||||
PMQLONG pReason)
|
||||
{
|
||||
log_debugf("mqhtpass_freeuser()");
|
||||
*pCompCode = MQCC_WARNING;
|
||||
*pReason = MQRC_NONE;
|
||||
*pContinuation = MQZCI_CONTINUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the authorization service is terminated.
|
||||
*/
|
||||
static void MQENTRY mqhtpass_terminate(
|
||||
MQHCONFIG hc,
|
||||
MQLONG Options,
|
||||
PMQCHAR pQMgrName,
|
||||
PMQBYTE pComponentData,
|
||||
PMQLONG pCompCode,
|
||||
PMQLONG pReason)
|
||||
{
|
||||
if (Options == MQZTO_PRIMARY)
|
||||
{
|
||||
log_infof("Terminating %s", NAME);
|
||||
log_close();
|
||||
}
|
||||
else {
|
||||
log_debugf("Terminating secondary");
|
||||
}
|
||||
*pCompCode = MQCC_OK;
|
||||
*pReason = MQRC_NONE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove trailing spaces from a string.
|
||||
*/
|
||||
static char *trim(char *s)
|
||||
{
|
||||
int i;
|
||||
for (i = strlen(s) - 1; i >= 0; i--)
|
||||
{
|
||||
if (s[i] == ' ')
|
||||
s[i] = 0;
|
||||
else
|
||||
break;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
© Copyright IBM Corporation 2017
|
||||
© Copyright IBM Corporation 2017, 2020
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -23,7 +23,7 @@ import (
|
||||
"os/exec"
|
||||
"strings"
|
||||
|
||||
"github.com/ibm-messaging/mq-container/internal/name"
|
||||
"github.com/ibm-messaging/mq-container/pkg/name"
|
||||
)
|
||||
|
||||
func queueManagerHealthy() (bool, error) {
|
||||
@@ -36,12 +36,12 @@ func queueManagerHealthy() (bool, error) {
|
||||
cmd := exec.Command("dspmq", "-n", "-m", name)
|
||||
// Run the command and wait for completion
|
||||
out, err := cmd.CombinedOutput()
|
||||
fmt.Printf("%s", out)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return false, err
|
||||
}
|
||||
fmt.Printf("%s", out)
|
||||
if !strings.Contains(string(out), "(RUNNING)") {
|
||||
if !strings.Contains(string(out), "(RUNNING)") && !strings.Contains(string(out), "(RUNNING AS STANDBY)") && !strings.Contains(string(out), "(STARTING)") && !strings.Contains(string(out), "(REPLICA)") {
|
||||
return false, nil
|
||||
}
|
||||
return true, nil
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
© Copyright IBM Corporation 2017, 2018
|
||||
© Copyright IBM Corporation 2017, 2019
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -23,6 +23,7 @@ import (
|
||||
"os"
|
||||
|
||||
"github.com/ibm-messaging/mq-container/internal/ready"
|
||||
"github.com/ibm-messaging/mq-container/pkg/name"
|
||||
)
|
||||
|
||||
func main() {
|
||||
@@ -31,14 +32,30 @@ func main() {
|
||||
if !r || err != nil {
|
||||
os.Exit(1)
|
||||
}
|
||||
// Check if the queue manager has a running listener
|
||||
conn, err := net.Dial("tcp", "127.0.0.1:1414")
|
||||
name, err := name.GetQueueManagerName()
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
err = conn.Close()
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
|
||||
// Check if the queue manager has a running listener
|
||||
if active, _ := ready.IsRunningAsActiveQM(name); active {
|
||||
conn, err := net.Dial("tcp", "127.0.0.1:1414")
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
err = conn.Close()
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
} else if standby, _ := ready.IsRunningAsStandbyQM(name); standby {
|
||||
fmt.Printf("Detected queue manager running in standby mode")
|
||||
os.Exit(10)
|
||||
} else if replica, _ := ready.IsRunningAsReplicaQM(name); replica {
|
||||
fmt.Printf("Detected queue manager running in replica mode")
|
||||
os.Exit(20)
|
||||
} else {
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
58
cmd/chkmqstarted/main.go
Normal file
58
cmd/chkmqstarted/main.go
Normal file
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
© Copyright IBM Corporation 2021
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// chkmqstarted checks that MQ has successfully started, by checking the output of the "dspmq" command
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
|
||||
"github.com/ibm-messaging/mq-container/pkg/name"
|
||||
)
|
||||
|
||||
func queueManagerStarted() (bool, error) {
|
||||
name, err := name.GetQueueManagerName()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
// Specify the queue manager name, just in case someone's created a second queue manager
|
||||
// #nosec G204
|
||||
cmd := exec.Command("dspmq", "-n", "-m", name)
|
||||
// Run the command and wait for completion
|
||||
out, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return false, err
|
||||
}
|
||||
if !strings.Contains(string(out), "(RUNNING)") && !strings.Contains(string(out), "(RUNNING AS STANDBY)") && !strings.Contains(string(out), "(STARTING)") && !strings.Contains(string(out), "(REPLICA)") {
|
||||
return false, nil
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
started, err := queueManagerStarted()
|
||||
if err != nil {
|
||||
os.Exit(2)
|
||||
}
|
||||
if !started {
|
||||
os.Exit(1)
|
||||
}
|
||||
os.Exit(0)
|
||||
}
|
||||
@@ -1,68 +0,0 @@
|
||||
/*
|
||||
© Copyright IBM Corporation 2017, 2019
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
package main
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
containerruntime "github.com/ibm-messaging/mq-container/internal/containerruntime"
|
||||
"github.com/ibm-messaging/mq-container/internal/user"
|
||||
)
|
||||
|
||||
func logContainerDetails() {
|
||||
log.Printf("CPU architecture: %v", runtime.GOARCH)
|
||||
kv, err := containerruntime.GetKernelVersion()
|
||||
if err == nil {
|
||||
log.Printf("Linux kernel version: %v", kv)
|
||||
}
|
||||
cr, err := containerruntime.GetContainerRuntime()
|
||||
if err == nil {
|
||||
log.Printf("Container runtime: %v", cr)
|
||||
}
|
||||
bi, err := containerruntime.GetBaseImage()
|
||||
if err == nil {
|
||||
log.Printf("Base image: %v", bi)
|
||||
}
|
||||
u, err := user.GetUser()
|
||||
if err == nil {
|
||||
if len(u.SupplementalGID) == 0 {
|
||||
log.Printf("Running as user ID %v (%v) with primary group %v", u.UID, u.Name, u.PrimaryGID)
|
||||
} else {
|
||||
log.Printf("Running as user ID %v (%v) with primary group %v, and supplementary groups %v", u.UID, u.Name, u.PrimaryGID, strings.Join(u.SupplementalGID, ","))
|
||||
}
|
||||
}
|
||||
caps, err := containerruntime.GetCapabilities()
|
||||
capLogged := false
|
||||
if err == nil {
|
||||
for k, v := range caps {
|
||||
if len(v) > 0 {
|
||||
log.Printf("Capabilities (%s set): %v", strings.ToLower(k), strings.Join(v, ","))
|
||||
capLogged = true
|
||||
}
|
||||
}
|
||||
if !capLogged {
|
||||
log.Print("Capabilities: none")
|
||||
}
|
||||
} else {
|
||||
log.Errorf("Error getting capabilities: %v", err)
|
||||
}
|
||||
sc, err := containerruntime.GetSeccomp()
|
||||
if err == nil {
|
||||
log.Printf("seccomp enforcing mode: %v", sc)
|
||||
}
|
||||
log.Printf("Process security attributes: %v", containerruntime.GetSecurityAttributes())
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
© Copyright IBM Corporation 2018, 2019
|
||||
© Copyright IBM Corporation 2018, 2021
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -19,37 +19,16 @@ import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"syscall"
|
||||
|
||||
"github.com/ibm-messaging/mq-container/internal/command"
|
||||
"github.com/ibm-messaging/mq-container/internal/logger"
|
||||
"github.com/ibm-messaging/mq-container/internal/name"
|
||||
"github.com/ibm-messaging/mq-container/internal/htpasswd"
|
||||
"github.com/ibm-messaging/mq-container/pkg/containerruntimelogger"
|
||||
"github.com/ibm-messaging/mq-container/pkg/logger"
|
||||
"github.com/ibm-messaging/mq-container/pkg/name"
|
||||
)
|
||||
|
||||
var log *logger.Logger
|
||||
|
||||
func setPassword(user string, password string) error {
|
||||
// #nosec G204
|
||||
cmd := exec.Command("sudo", "chpasswd")
|
||||
stdin, err := cmd.StdinPipe()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Fprintf(stdin, "%s:%s", user, password)
|
||||
err = stdin.Close()
|
||||
if err != nil {
|
||||
log.Errorf("Error closing password stdin: %v", err)
|
||||
}
|
||||
out, _, err := command.RunCmd(cmd)
|
||||
if err != nil {
|
||||
// Include the command output in the error
|
||||
return fmt.Errorf("%v: %v", err.Error(), out)
|
||||
}
|
||||
log.Printf("Set password for \"%v\" user", user)
|
||||
return nil
|
||||
}
|
||||
|
||||
func getLogFormat() string {
|
||||
return os.Getenv("LOG_FORMAT")
|
||||
}
|
||||
@@ -88,11 +67,6 @@ func configureLogger() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func configureWeb(qmName string) error {
|
||||
out := "/etc/mqm/web/installations/Installation1/angular.persistence/admin.json"
|
||||
return processTemplateFile("/etc/mqm/admin.json.tpl", out, map[string]string{"QueueManagerName": qmName})
|
||||
}
|
||||
|
||||
func logTerminationf(format string, args ...interface{}) {
|
||||
logTermination(fmt.Sprintf(format, args...))
|
||||
}
|
||||
@@ -103,6 +77,7 @@ func logTermination(args ...interface{}) {
|
||||
// Write the message to the termination log. This is not the default place
|
||||
// that Kubernetes will look for termination information.
|
||||
log.Debugf("Writing termination message: %v", msg)
|
||||
// #nosec G306 - its a read by owner/s group, and pose no harm.
|
||||
err := ioutil.WriteFile("/run/termination-log", []byte(msg), 0660)
|
||||
if err != nil {
|
||||
log.Debug(err)
|
||||
@@ -117,19 +92,30 @@ func doMain() error {
|
||||
return err
|
||||
}
|
||||
|
||||
logContainerDetails()
|
||||
err = containerruntimelogger.LogContainerDetails(log)
|
||||
if err != nil {
|
||||
logTermination(err)
|
||||
return err
|
||||
}
|
||||
|
||||
adminPassword, set := os.LookupEnv("MQ_ADMIN_PASSWORD")
|
||||
if set {
|
||||
err = setPassword("admin", adminPassword)
|
||||
if !set {
|
||||
adminPassword = "passw0rd"
|
||||
err = os.Setenv("MQ_ADMIN_PASSWORD", adminPassword)
|
||||
if err != nil {
|
||||
logTerminationf("Error setting admin password: %v", err)
|
||||
logTerminationf("Error setting admin password variable: %v", err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
err = htpasswd.SetPassword("admin", adminPassword, false)
|
||||
if err != nil {
|
||||
logTerminationf("Error setting admin password: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
appPassword, set := os.LookupEnv("MQ_APP_PASSWORD")
|
||||
if set {
|
||||
err = setPassword("app", appPassword)
|
||||
err = htpasswd.SetPassword("app", appPassword, false)
|
||||
if err != nil {
|
||||
logTerminationf("Error setting app password: %v", err)
|
||||
return err
|
||||
@@ -142,26 +128,6 @@ func doMain() error {
|
||||
return err
|
||||
}
|
||||
|
||||
name, err := name.GetQueueManagerName()
|
||||
if err != nil {
|
||||
logTerminationf("Error getting queue manager name: %v", err)
|
||||
return err
|
||||
}
|
||||
ks, set := os.LookupEnv("MQ_TLS_KEYSTORE")
|
||||
if set {
|
||||
err = configureTLS(name, ks, os.Getenv("MQ_TLS_PASSPHRASE"))
|
||||
if err != nil {
|
||||
logTerminationf("Error configuring TLS: %v", err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
err = configureWeb(name)
|
||||
if err != nil {
|
||||
logTermination("Error configuring admin.json")
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -174,7 +140,7 @@ func main() {
|
||||
} else {
|
||||
// Replace this process with runmqserver
|
||||
// #nosec G204
|
||||
err = syscall.Exec("/usr/local/bin/runmqserver", []string{"runmqserver", "-dev"}, os.Environ())
|
||||
err = syscall.Exec("/usr/local/bin/runmqserver", []string{"runmqserver", "-nologruntime", "-dev"}, os.Environ())
|
||||
if err != nil {
|
||||
log.Errorf("Error replacing this process with runmqserver: %v", err)
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
© Copyright IBM Corporation 2018
|
||||
© Copyright IBM Corporation 2018, 2019
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -17,6 +17,8 @@ package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/ibm-messaging/mq-container/internal/mqtemplate"
|
||||
)
|
||||
|
||||
func updateMQSC(appPasswordRequired bool) error {
|
||||
@@ -30,7 +32,7 @@ func updateMQSC(appPasswordRequired bool) error {
|
||||
if os.Getenv("MQ_DEV") == "true" {
|
||||
const mqscTemplate string = mqsc + ".tpl"
|
||||
// Re-configure channel if app password not set
|
||||
err := processTemplateFile(mqsc+".tpl", mqsc, map[string]string{"ChckClnt": checkClient})
|
||||
err := mqtemplate.ProcessTemplateFile(mqsc+".tpl", mqsc, map[string]string{"ChckClnt": checkClient}, log)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -1,164 +0,0 @@
|
||||
/*
|
||||
© Copyright IBM Corporation 2018
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/ibm-messaging/mq-container/internal/command"
|
||||
)
|
||||
|
||||
func configureWebTLS(cms *KeyStore) error {
|
||||
dir := "/run/runmqdevserver/tls"
|
||||
ks := NewJKSKeyStore(filepath.Join(dir, "key.jks"), cms.Password)
|
||||
ts := NewJKSKeyStore(filepath.Join(dir, "trust.jks"), cms.Password)
|
||||
|
||||
log.Debug("Creating key store")
|
||||
err := ks.Create()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Debug("Creating trust store")
|
||||
err = ts.Create()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Debug("Importing keys")
|
||||
err = ks.Import(cms.Filename, cms.Password)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
webConfigDir := "/etc/mqm/web/installations/Installation1/servers/mqweb"
|
||||
tlsConfig := filepath.Join(webConfigDir, "tls.xml")
|
||||
newTLSConfig := filepath.Join(webConfigDir, "tls-dev.xml")
|
||||
err = os.Remove(tlsConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// we symlink here to prevent issues on restart
|
||||
err = os.Symlink(newTLSConfig, tlsConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
mqmUID, mqmGID, err := command.LookupMQM()
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return err
|
||||
}
|
||||
err = os.Chown(tlsConfig, mqmUID, mqmGID)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func configureTLS(qmName string, inputFile string, passPhrase string) error {
|
||||
log.Debug("Configuring TLS")
|
||||
|
||||
_, err := os.Stat(inputFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// TODO: Use a persisted file (on the volume) instead?
|
||||
dir := "/run/runmqdevserver/tls"
|
||||
keyFile := filepath.Join(dir, "key.kdb")
|
||||
|
||||
_, err = os.Stat(dir)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
// #nosec G301
|
||||
err = os.MkdirAll(dir, 0770)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
mqmUID, mqmGID, err := command.LookupMQM()
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return err
|
||||
}
|
||||
err = os.Chown(dir, mqmUID, mqmGID)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
cms := NewCMSKeyStore(keyFile, passPhrase)
|
||||
|
||||
err = cms.Create()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = cms.CreateStash()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = cms.Import(inputFile, passPhrase)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
labels, err := cms.GetCertificateLabels()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(labels) == 0 {
|
||||
return fmt.Errorf("unable to find certificate label")
|
||||
}
|
||||
log.Debugf("Renaming certificate from %v", labels[0])
|
||||
const newLabel string = "devcert"
|
||||
err = cms.RenameCertificate(labels[0], newLabel)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var sslCipherSpec string
|
||||
if os.Getenv("MQ_DEV") == "true" {
|
||||
sslCipherSpec = "TLS_RSA_WITH_AES_128_CBC_SHA256"
|
||||
} else {
|
||||
sslCipherSpec = "' '"
|
||||
}
|
||||
|
||||
const mqsc string = "/etc/mqm/20-dev-tls.mqsc"
|
||||
const mqscTemplate string = mqsc + ".tpl"
|
||||
|
||||
err = processTemplateFile(mqscTemplate, mqsc, map[string]string{
|
||||
"SSLKeyR": filepath.Join(dir, "key"),
|
||||
"CertificateLabel": newLabel,
|
||||
"SSLCipherSpec": sslCipherSpec,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = configureWebTLS(cms)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
© Copyright IBM Corporation 2017, 2018
|
||||
© Copyright IBM Corporation 2017, 2020
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -17,16 +17,10 @@ package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"syscall"
|
||||
|
||||
"github.com/ibm-messaging/mq-container/internal/command"
|
||||
)
|
||||
|
||||
func createVolume(path string) error {
|
||||
dataPath := filepath.Join(path, "data")
|
||||
fi, err := os.Stat(dataPath)
|
||||
func createVolume(dataPath string) error {
|
||||
_, err := os.Stat(dataPath)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
// #nosec G301
|
||||
@@ -38,25 +32,5 @@ func createVolume(path string) error {
|
||||
return err
|
||||
}
|
||||
}
|
||||
fi, err = os.Stat(dataPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
sys := fi.Sys()
|
||||
if sys != nil && runtime.GOOS == "linux" {
|
||||
stat := sys.(*syscall.Stat_t)
|
||||
mqmUID, mqmGID, err := command.LookupMQM()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Debugf("mqm user is %v (%v)", mqmUID, mqmGID)
|
||||
if int(stat.Uid) != mqmUID || int(stat.Gid) != mqmGID {
|
||||
err = os.Chown(dataPath, mqmUID, mqmGID)
|
||||
if err != nil {
|
||||
log.Printf("Error: Unable to change ownership of %v", dataPath)
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
© Copyright IBM Corporation 2017, 2019
|
||||
© Copyright IBM Corporation 2017, 2021
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -23,11 +23,13 @@ import (
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/ibm-messaging/mq-container/internal/command"
|
||||
"github.com/ibm-messaging/mq-container/internal/logger"
|
||||
"github.com/ibm-messaging/mq-container/internal/mqini"
|
||||
"github.com/ibm-messaging/mq-container/pkg/logger"
|
||||
"github.com/ibm-messaging/mq-container/pkg/mqini"
|
||||
)
|
||||
|
||||
// var debug = false
|
||||
@@ -44,6 +46,7 @@ func logTermination(args ...interface{}) {
|
||||
// Write the message to the termination log. This is not the default place
|
||||
// that Kubernetes will look for termination information.
|
||||
log.Debugf("Writing termination message: %v", msg)
|
||||
// #nosec G306 - its a read by owner/s group, and pose no harm.
|
||||
err := ioutil.WriteFile("/run/termination-log", []byte(msg), 0660)
|
||||
if err != nil {
|
||||
log.Debug(err)
|
||||
@@ -59,14 +62,33 @@ func getLogFormat() string {
|
||||
return os.Getenv("LOG_FORMAT")
|
||||
}
|
||||
|
||||
func formatSimple(datetime string, message string) string {
|
||||
return fmt.Sprintf("%v %v\n", datetime, message)
|
||||
// formatBasic formats a log message parsed from JSON, as "basic" text
|
||||
func formatBasic(obj map[string]interface{}) string {
|
||||
// Emulate the MQ "MessageDetail=Extended" option, by appending inserts to the message
|
||||
// This is important for certain messages, where key details are only available in the extended message content
|
||||
inserts := make([]string, 0)
|
||||
for k, v := range obj {
|
||||
if strings.HasPrefix(k, "ibm_commentInsert") {
|
||||
inserts = append(inserts, fmt.Sprintf("%s(%v)", strings.Replace(k, "ibm_comment", "Comment", 1), obj[k]))
|
||||
} else if strings.HasPrefix(k, "ibm_arithInsert") {
|
||||
if v.(float64) != 0 {
|
||||
inserts = append(inserts, fmt.Sprintf("%s(%v)", strings.Replace(k, "ibm_arith", "Arith", 1), obj[k]))
|
||||
}
|
||||
}
|
||||
}
|
||||
sort.Strings(inserts)
|
||||
if len(inserts) > 0 {
|
||||
return fmt.Sprintf("%s %s [%v]\n", obj["ibm_datetime"], obj["message"], strings.Join(inserts, ", "))
|
||||
}
|
||||
// Convert time zone information from some logs (e.g. Liberty) for consistency
|
||||
obj["ibm_datetime"] = strings.Replace(obj["ibm_datetime"].(string), "+0000", "Z", 1)
|
||||
return fmt.Sprintf("%s %s\n", obj["ibm_datetime"], obj["message"])
|
||||
}
|
||||
|
||||
// mirrorSystemErrorLogs starts a goroutine to mirror the contents of the MQ system error logs
|
||||
func mirrorSystemErrorLogs(ctx context.Context, wg *sync.WaitGroup, mf mirrorFunc) (chan error, error) {
|
||||
// Always use the JSON log as the source
|
||||
return mirrorLog(ctx, wg, "/var/mqm/errors/AMQERR01.json", false, mf)
|
||||
return mirrorLog(ctx, wg, "/var/mqm/errors/AMQERR01.json", false, mf, false)
|
||||
}
|
||||
|
||||
// mirrorQueueManagerErrorLogs starts a goroutine to mirror the contents of the MQ queue manager error logs
|
||||
@@ -78,7 +100,17 @@ func mirrorQueueManagerErrorLogs(ctx context.Context, wg *sync.WaitGroup, name s
|
||||
return nil, err
|
||||
}
|
||||
f := filepath.Join(mqini.GetErrorLogDirectory(qm), "AMQERR01.json")
|
||||
return mirrorLog(ctx, wg, f, fromStart, mf)
|
||||
return mirrorLog(ctx, wg, f, fromStart, mf, true)
|
||||
}
|
||||
|
||||
// mirrorHTPasswdLogs starts a goroutine to mirror the contents of the MQ HTPasswd authorization service's log
|
||||
func mirrorHTPasswdLogs(ctx context.Context, wg *sync.WaitGroup, name string, fromStart bool, mf mirrorFunc) (chan error, error) {
|
||||
return mirrorLog(ctx, wg, "/var/mqm/errors/mqhtpass.json", false, mf, true)
|
||||
}
|
||||
|
||||
// mirrorWebServerLogs starts a goroutine to mirror the contents of the Liberty web server messages.log
|
||||
func mirrorWebServerLogs(ctx context.Context, wg *sync.WaitGroup, name string, fromStart bool, mf mirrorFunc) (chan error, error) {
|
||||
return mirrorLog(ctx, wg, "/var/mqm/web/installations/Installation1/servers/mqweb/logs/messages.log", false, mf, true)
|
||||
}
|
||||
|
||||
func getDebug() bool {
|
||||
@@ -99,21 +131,49 @@ func configureLogger(name string) (mirrorFunc, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return log.LogDirect, nil
|
||||
return func(msg string, isQMLog bool) bool {
|
||||
// Check if the message is JSON
|
||||
if len(msg) > 0 && msg[0] == '{' {
|
||||
obj, err := processLogMessage(msg)
|
||||
if err == nil && isQMLog && filterQMLogMessage(obj) {
|
||||
return false
|
||||
}
|
||||
if err != nil {
|
||||
log.Printf("Failed to unmarshall JSON in log message - %v", msg)
|
||||
} else {
|
||||
fmt.Println(msg)
|
||||
}
|
||||
} else {
|
||||
// The log being mirrored isn't JSON, so wrap it in a simple JSON message
|
||||
// MQ error logs are usually JSON, but this is useful for Liberty logs - usually expect WLP_LOGGING_MESSAGE_FORMAT=JSON to be set when mirroring Liberty logs.
|
||||
fmt.Printf("{\"message\":\"%s\"}\n", msg)
|
||||
}
|
||||
return true
|
||||
}, nil
|
||||
case "basic":
|
||||
log, err = logger.NewLogger(os.Stderr, d, false, name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return func(msg string) {
|
||||
// Parse the JSON message, and print a simplified version
|
||||
var obj map[string]interface{}
|
||||
err := json.Unmarshal([]byte(msg), &obj)
|
||||
if err != nil {
|
||||
fmt.Printf("Failed to Unmarshall JSON - %v", err)
|
||||
return func(msg string, isQMLog bool) bool {
|
||||
// Check if the message is JSON
|
||||
if len(msg) > 0 && msg[0] == '{' {
|
||||
// Parse the JSON message, and print a simplified version
|
||||
obj, err := processLogMessage(msg)
|
||||
if err == nil && isQMLog && filterQMLogMessage(obj) {
|
||||
return false
|
||||
}
|
||||
if err != nil {
|
||||
log.Printf("Failed to unmarshall JSON in log message - %v", err)
|
||||
} else {
|
||||
fmt.Printf(formatBasic(obj))
|
||||
}
|
||||
} else {
|
||||
fmt.Printf(formatSimple(obj["ibm_datetime"].(string), obj["message"].(string)))
|
||||
// The log being mirrored isn't JSON, so just print it.
|
||||
// MQ error logs are usually JSON, but this is useful for Liberty logs - usually expect WLP_LOGGING_MESSAGE_FORMAT=JSON to be set when mirroring Liberty logs.
|
||||
fmt.Println(msg)
|
||||
}
|
||||
return true
|
||||
}, nil
|
||||
default:
|
||||
log, err = logger.NewLogger(os.Stdout, d, false, name)
|
||||
@@ -124,33 +184,58 @@ func configureLogger(name string) (mirrorFunc, error) {
|
||||
}
|
||||
}
|
||||
|
||||
func logDiagnostics() {
|
||||
log.Debug("--- Start Diagnostics ---")
|
||||
|
||||
// show the directory ownership/permissions
|
||||
// #nosec G104
|
||||
out, _, _ := command.Run("ls", "-l", "/mnt/")
|
||||
log.Debugf("/mnt/:\n%s", out)
|
||||
// #nosec G104
|
||||
out, _, _ = command.Run("ls", "-l", "/mnt/mqm")
|
||||
log.Debugf("/mnt/mqm:\n%s", out)
|
||||
// #nosec G104
|
||||
out, _, _ = command.Run("ls", "-l", "/mnt/mqm/data")
|
||||
log.Debugf("/mnt/mqm/data:\n%s", out)
|
||||
// #nosec G104
|
||||
out, _, _ = command.Run("ls", "-l", "/var/mqm")
|
||||
log.Debugf("/var/mqm:\n%s", out)
|
||||
// #nosec G104
|
||||
out, _, _ = command.Run("ls", "-l", "/var/mqm/errors")
|
||||
log.Debugf("/var/mqm/errors:\n%s", out)
|
||||
|
||||
// Print out summary of any FDCs
|
||||
// #nosec G204
|
||||
cmd := exec.Command("/opt/mqm/bin/ffstsummary")
|
||||
cmd.Dir = "/var/mqm/errors"
|
||||
// #nosec G104
|
||||
outB, _ := cmd.CombinedOutput()
|
||||
log.Debugf("ffstsummary:\n%s", string(outB))
|
||||
|
||||
log.Debug("--- End Diagnostics ---")
|
||||
func processLogMessage(msg string) (map[string]interface{}, error) {
|
||||
var obj map[string]interface{}
|
||||
err := json.Unmarshal([]byte(msg), &obj)
|
||||
return obj, err
|
||||
}
|
||||
|
||||
func filterQMLogMessage(obj map[string]interface{}) bool {
|
||||
hostname, err := os.Hostname()
|
||||
if os.Getenv("MQ_MULTI_INSTANCE") == "true" && err == nil && !strings.Contains(obj["host"].(string), hostname) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func logDiagnostics() {
|
||||
if getDebug() {
|
||||
log.Debug("--- Start Diagnostics ---")
|
||||
|
||||
// show the directory ownership/permissions
|
||||
// #nosec G104
|
||||
out, _, _ := command.Run("ls", "-l", "/mnt/")
|
||||
log.Debugf("/mnt/:\n%s", out)
|
||||
// #nosec G104
|
||||
out, _, _ = command.Run("ls", "-l", "/mnt/mqm")
|
||||
log.Debugf("/mnt/mqm:\n%s", out)
|
||||
// #nosec G104
|
||||
out, _, _ = command.Run("ls", "-l", "/mnt/mqm/data")
|
||||
log.Debugf("/mnt/mqm/data:\n%s", out)
|
||||
// #nosec G104
|
||||
out, _, _ = command.Run("ls", "-l", "/mnt/mqm-log/log")
|
||||
log.Debugf("/mnt/mqm-log/log:\n%s", out)
|
||||
// #nosec G104
|
||||
out, _, _ = command.Run("ls", "-l", "/mnt/mqm-data/qmgrs")
|
||||
log.Debugf("/mnt/mqm-data/qmgrs:\n%s", out)
|
||||
// #nosec G104
|
||||
out, _, _ = command.Run("ls", "-l", "/var/mqm")
|
||||
log.Debugf("/var/mqm:\n%s", out)
|
||||
// #nosec G104
|
||||
out, _, _ = command.Run("ls", "-l", "/var/mqm/errors")
|
||||
log.Debugf("/var/mqm/errors:\n%s", out)
|
||||
// #nosec G104
|
||||
out, _, _ = command.Run("ls", "-l", "/etc/mqm")
|
||||
log.Debugf("/etc/mqm:\n%s", out)
|
||||
|
||||
// Print out summary of any FDCs
|
||||
// #nosec G204
|
||||
cmd := exec.Command("/opt/mqm/bin/ffstsummary")
|
||||
cmd.Dir = "/var/mqm/errors"
|
||||
// #nosec G104
|
||||
outB, _ := cmd.CombinedOutput()
|
||||
log.Debugf("ffstsummary:\n%s", string(outB))
|
||||
|
||||
log.Debug("--- End Diagnostics ---")
|
||||
}
|
||||
}
|
||||
|
||||
55
cmd/runmqserver/logging_test.go
Normal file
55
cmd/runmqserver/logging_test.go
Normal file
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
© Copyright IBM Corporation 2020
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var formatBasicTests = []struct {
|
||||
in []byte
|
||||
outContains string
|
||||
}{
|
||||
{
|
||||
[]byte("{\"ibm_datetime\":\"2020/06/24 00:00:00\",\"message\":\"Hello world\"}"),
|
||||
"Hello",
|
||||
},
|
||||
{
|
||||
[]byte("{\"ibm_datetime\":\"2020/06/24 00:00:00\",\"message\":\"Hello world\", \"ibm_commentInsert1\":\"foo\"}"),
|
||||
"CommentInsert1(foo)",
|
||||
},
|
||||
{
|
||||
[]byte("{\"ibm_datetime\":\"2020/06/24 00:00:00\",\"message\":\"Hello world\", \"ibm_arithInsert1\":1}"),
|
||||
"ArithInsert1(1)",
|
||||
},
|
||||
}
|
||||
|
||||
func TestFormatBasic(t *testing.T) {
|
||||
for i, table := range formatBasicTests {
|
||||
t.Run(fmt.Sprintf("%v", i), func(t *testing.T) {
|
||||
var inObj map[string]interface{}
|
||||
json.Unmarshal(table.in, &inObj)
|
||||
t.Logf("Unmarshalled: %+v", inObj)
|
||||
out := formatBasic(inObj)
|
||||
if !strings.Contains(out, table.outContains) {
|
||||
t.Errorf("formatBasic() with input=%v - expected output to contain %v, got %v", string(table.in), table.outContains, out)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
© Copyright IBM Corporation 2017, 2019
|
||||
© Copyright IBM Corporation 2017, 2021
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -24,15 +24,19 @@ import (
|
||||
"os"
|
||||
"sync"
|
||||
|
||||
"github.com/ibm-messaging/mq-container/internal/ha"
|
||||
"github.com/ibm-messaging/mq-container/internal/metrics"
|
||||
"github.com/ibm-messaging/mq-container/internal/name"
|
||||
"github.com/ibm-messaging/mq-container/internal/ready"
|
||||
"github.com/ibm-messaging/mq-container/internal/tls"
|
||||
"github.com/ibm-messaging/mq-container/pkg/containerruntimelogger"
|
||||
"github.com/ibm-messaging/mq-container/pkg/name"
|
||||
)
|
||||
|
||||
func doMain() error {
|
||||
var initFlag = flag.Bool("i", false, "initialize volume only, then exit")
|
||||
var infoFlag = flag.Bool("info", false, "Display debug info, then exit")
|
||||
var devFlag = flag.Bool("dev", false, "used when running this program from runmqdevserver to control log output")
|
||||
var noLogRuntimeFlag = flag.Bool("nologruntime", false, "used when running this program from another program, to control log output")
|
||||
var devFlag = flag.Bool("dev", false, "used when running this program from runmqdevserver to control how TLS is configured")
|
||||
flag.Parse()
|
||||
|
||||
name, nameErr := name.GetQueueManagerName()
|
||||
@@ -45,7 +49,7 @@ func doMain() error {
|
||||
// Check whether they only want debug info
|
||||
if *infoFlag {
|
||||
logVersionInfo()
|
||||
err = logContainerDetails()
|
||||
err = containerruntimelogger.LogContainerDetails(log)
|
||||
if err != nil {
|
||||
log.Printf("Error displaying container details: %v", err)
|
||||
}
|
||||
@@ -85,25 +89,53 @@ func doMain() error {
|
||||
// Enable diagnostic collecting on failure
|
||||
collectDiagOnFail = true
|
||||
|
||||
if *devFlag == false {
|
||||
err = logContainerDetails()
|
||||
if *noLogRuntimeFlag == false {
|
||||
err = containerruntimelogger.LogContainerDetails(log)
|
||||
if err != nil {
|
||||
logTermination(err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
err = createVolume("/mnt/mqm")
|
||||
err = createVolume("/mnt/mqm/data")
|
||||
if err != nil {
|
||||
logTermination(err)
|
||||
return err
|
||||
}
|
||||
err = createVolume("/mnt/mqm-log/log")
|
||||
if err != nil {
|
||||
logTermination(err)
|
||||
return err
|
||||
}
|
||||
err = createVolume("/mnt/mqm-data/qmgrs")
|
||||
if err != nil {
|
||||
logTermination(err)
|
||||
return err
|
||||
}
|
||||
|
||||
enableTraceCrtmqdir := os.Getenv("MQ_ENABLE_TRACE_CRTMQDIR")
|
||||
if enableTraceCrtmqdir == "true" || enableTraceCrtmqdir == "1" {
|
||||
err = startMQTrace()
|
||||
if err != nil {
|
||||
logTermination(err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
err = createDirStructure()
|
||||
if err != nil {
|
||||
logTermination(err)
|
||||
return err
|
||||
}
|
||||
|
||||
if enableTraceCrtmqdir == "true" || enableTraceCrtmqdir == "1" {
|
||||
err = endMQTrace()
|
||||
if err != nil {
|
||||
logTermination(err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// If init flag is set, exit now
|
||||
if *initFlag {
|
||||
return nil
|
||||
@@ -112,17 +144,55 @@ func doMain() error {
|
||||
// Print out versioning information
|
||||
logVersionInfo()
|
||||
|
||||
err = postInit(name)
|
||||
keyLabel, defaultCmsKeystore, defaultP12Truststore, err := tls.ConfigureDefaultTLSKeystores()
|
||||
if err != nil {
|
||||
logTermination(err)
|
||||
return err
|
||||
}
|
||||
|
||||
newQM, err := createQueueManager(name)
|
||||
err = tls.ConfigureTLS(keyLabel, defaultCmsKeystore, *devFlag, log)
|
||||
if err != nil {
|
||||
logTermination(err)
|
||||
return err
|
||||
}
|
||||
|
||||
err = postInit(name, keyLabel, defaultP12Truststore)
|
||||
if err != nil {
|
||||
logTermination(err)
|
||||
return err
|
||||
}
|
||||
|
||||
if os.Getenv("MQ_NATIVE_HA") == "true" {
|
||||
err = ha.ConfigureNativeHA(log)
|
||||
if err != nil {
|
||||
logTermination(err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
enableTraceCrtmqm := os.Getenv("MQ_ENABLE_TRACE_CRTMQM")
|
||||
if enableTraceCrtmqm == "true" || enableTraceCrtmqm == "1" {
|
||||
err = startMQTrace()
|
||||
if err != nil {
|
||||
logTermination(err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
newQM, err := createQueueManager(name, *devFlag)
|
||||
if err != nil {
|
||||
logTermination(err)
|
||||
return err
|
||||
}
|
||||
|
||||
if enableTraceCrtmqm == "true" || enableTraceCrtmqm == "1" {
|
||||
err = endMQTrace()
|
||||
if err != nil {
|
||||
logTermination(err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
var wg sync.WaitGroup
|
||||
defer func() {
|
||||
log.Debug("Waiting for log mirroring to complete")
|
||||
@@ -144,20 +214,60 @@ func doMain() error {
|
||||
logTermination(err)
|
||||
return err
|
||||
}
|
||||
if *devFlag {
|
||||
_, err = mirrorHTPasswdLogs(ctx, &wg, name, newQM, mf)
|
||||
if err != nil {
|
||||
logTermination(err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
// Recommended to use this option in conjunction with setting WLP_LOGGING_MESSAGE_FORMAT=JSON
|
||||
mirrorWebLog := os.Getenv("MQ_ENABLE_EMBEDDED_WEB_SERVER_LOG")
|
||||
if mirrorWebLog == "true" || mirrorWebLog == "1" {
|
||||
_, err = mirrorWebServerLogs(ctx, &wg, name, newQM, mf)
|
||||
if err != nil {
|
||||
logTermination(err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
err = updateCommandLevel()
|
||||
if err != nil {
|
||||
logTermination(err)
|
||||
return err
|
||||
}
|
||||
err = startQueueManager()
|
||||
|
||||
enableTraceStrmqm := os.Getenv("MQ_ENABLE_TRACE_STRMQM")
|
||||
if enableTraceStrmqm == "true" || enableTraceStrmqm == "1" {
|
||||
err = startMQTrace()
|
||||
if err != nil {
|
||||
logTermination(err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// This is a developer image only change
|
||||
// This workaround should be removed and handled via <crtmqm -ii>, when inimerge is ready to handle stanza ordering
|
||||
if *devFlag {
|
||||
err = updateQMini(name)
|
||||
if err != nil {
|
||||
logTermination(err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
err = startQueueManager(name)
|
||||
if err != nil {
|
||||
logTermination(err)
|
||||
return err
|
||||
}
|
||||
err = configureQueueManager()
|
||||
if err != nil {
|
||||
logTermination(err)
|
||||
return err
|
||||
|
||||
if enableTraceStrmqm == "true" || enableTraceStrmqm == "1" {
|
||||
err = endMQTrace()
|
||||
if err != nil {
|
||||
logTermination(err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
enableMetrics := os.Getenv("MQ_ENABLE_METRICS")
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
© Copyright IBM Corporation 2017, 2018
|
||||
© Copyright IBM Corporation 2017, 2019
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -23,7 +23,7 @@ import (
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
"github.com/ibm-messaging/mq-container/internal/logger"
|
||||
"github.com/ibm-messaging/mq-container/pkg/logger"
|
||||
)
|
||||
|
||||
var test *bool
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
© Copyright IBM Corporation 2018
|
||||
© Copyright IBM Corporation 2018, 2019
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -49,16 +49,17 @@ func waitForFile(ctx context.Context, path string) (os.FileInfo, error) {
|
||||
}
|
||||
}
|
||||
|
||||
type mirrorFunc func(msg string)
|
||||
type mirrorFunc func(msg string, isQMLog bool) bool
|
||||
|
||||
// mirrorAvailableMessages prints lines from the file, until no more are available
|
||||
func mirrorAvailableMessages(f *os.File, mf mirrorFunc) {
|
||||
func mirrorAvailableMessages(f *os.File, mf mirrorFunc, isQMLog bool) {
|
||||
scanner := bufio.NewScanner(f)
|
||||
count := 0
|
||||
for scanner.Scan() {
|
||||
t := scanner.Text()
|
||||
mf(t)
|
||||
count++
|
||||
if mf(t, isQMLog) {
|
||||
count++
|
||||
}
|
||||
}
|
||||
if count > 0 {
|
||||
log.Debugf("Mirrored %v log entries from %v", count, f.Name())
|
||||
@@ -73,7 +74,7 @@ func mirrorAvailableMessages(f *os.File, mf mirrorFunc) {
|
||||
// mirrorLog tails the specified file, and logs each line to stdout.
|
||||
// This is useful for usability, as the container console log can show
|
||||
// messages from the MQ error logs.
|
||||
func mirrorLog(ctx context.Context, wg *sync.WaitGroup, path string, fromStart bool, mf mirrorFunc) (chan error, error) {
|
||||
func mirrorLog(ctx context.Context, wg *sync.WaitGroup, path string, fromStart bool, mf mirrorFunc, isQMLog bool) (chan error, error) {
|
||||
errorChannel := make(chan error, 1)
|
||||
var offset int64 = -1
|
||||
var f *os.File
|
||||
@@ -94,6 +95,7 @@ func mirrorLog(ctx context.Context, wg *sync.WaitGroup, path string, fromStart b
|
||||
// the file is open before the queue manager is created or started.
|
||||
// Otherwise, there would be the potential for a nearly-full file to
|
||||
// rotate before the goroutine had a chance to open it.
|
||||
// #nosec G304 - no harm, we open readonly and check error.
|
||||
f, err = os.OpenFile(path, os.O_RDONLY, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -121,6 +123,7 @@ func mirrorLog(ctx context.Context, wg *sync.WaitGroup, path string, fromStart b
|
||||
return
|
||||
}
|
||||
log.Debugf("File exists: %v, %v", path, fi.Size())
|
||||
// #nosec G304 - no harm, we open readonly and check error.
|
||||
f, err = os.OpenFile(path, os.O_RDONLY, 0)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
@@ -147,7 +150,7 @@ func mirrorLog(ctx context.Context, wg *sync.WaitGroup, path string, fromStart b
|
||||
closing := false
|
||||
for {
|
||||
// If there's already data there, mirror it now.
|
||||
mirrorAvailableMessages(f, mf)
|
||||
mirrorAvailableMessages(f, mf, isQMLog)
|
||||
// Wait for the new log file (after rotation)
|
||||
newFI, err := waitForFile(ctx, path)
|
||||
if err != nil {
|
||||
@@ -161,13 +164,14 @@ func mirrorLog(ctx context.Context, wg *sync.WaitGroup, path string, fromStart b
|
||||
// log rotation happens before we can open the new file, then we
|
||||
// could skip all those messages. This could happen with a very small
|
||||
// MQ error log size.
|
||||
mirrorAvailableMessages(f, mf)
|
||||
mirrorAvailableMessages(f, mf, isQMLog)
|
||||
err = f.Close()
|
||||
if err != nil {
|
||||
log.Errorf("Unable to close mirror file handle: %v", err)
|
||||
}
|
||||
// Re-open file
|
||||
log.Debugf("Re-opening error log file %v", path)
|
||||
// #nosec G304 - no harm, we open readonly and check error.
|
||||
f, err = os.OpenFile(path, os.O_RDONLY, 0)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
@@ -176,7 +180,7 @@ func mirrorLog(ctx context.Context, wg *sync.WaitGroup, path string, fromStart b
|
||||
}
|
||||
fi = newFI
|
||||
// Don't seek this time, because we know it's a new file
|
||||
mirrorAvailableMessages(f, mf)
|
||||
mirrorAvailableMessages(f, mf, isQMLog)
|
||||
}
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
© Copyright IBM Corporation 2018
|
||||
© Copyright IBM Corporation 2018, 2019
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -41,9 +41,10 @@ func TestMirrorLogWithoutRotation(t *testing.T) {
|
||||
count := 0
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
var wg sync.WaitGroup
|
||||
_, err = mirrorLog(ctx, &wg, tmp.Name(), true, func(msg string) {
|
||||
_, err = mirrorLog(ctx, &wg, tmp.Name(), true, func(msg string, isQMLog bool) bool {
|
||||
count++
|
||||
})
|
||||
return true
|
||||
}, false)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -82,9 +83,10 @@ func TestMirrorLogWithRotation(t *testing.T) {
|
||||
count := 0
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
var wg sync.WaitGroup
|
||||
_, err = mirrorLog(ctx, &wg, tmp.Name(), true, func(msg string) {
|
||||
_, err = mirrorLog(ctx, &wg, tmp.Name(), true, func(msg string, isQMLog bool) bool {
|
||||
count++
|
||||
})
|
||||
return true
|
||||
}, false)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -135,9 +137,10 @@ func testMirrorLogExistingFile(t *testing.T, newQM bool) int {
|
||||
count := 0
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
var wg sync.WaitGroup
|
||||
_, err = mirrorLog(ctx, &wg, tmp.Name(), newQM, func(msg string) {
|
||||
_, err = mirrorLog(ctx, &wg, tmp.Name(), newQM, func(msg string, isQMLog bool) bool {
|
||||
count++
|
||||
})
|
||||
return true
|
||||
}, false)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -179,8 +182,9 @@ func TestMirrorLogCancelWhileWaiting(t *testing.T) {
|
||||
cancel()
|
||||
wg.Wait()
|
||||
}()
|
||||
_, err := mirrorLog(ctx, &wg, "fake.log", true, func(msg string) {
|
||||
})
|
||||
_, err := mirrorLog(ctx, &wg, "fake.log", true, func(msg string, isQMLog bool) bool {
|
||||
return true
|
||||
}, false)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
// +build mqdev
|
||||
|
||||
/*
|
||||
© Copyright IBM Corporation 2018
|
||||
© Copyright IBM Corporation 2018, 2019
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -19,23 +17,31 @@ package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/ibm-messaging/mq-container/internal/tls"
|
||||
)
|
||||
|
||||
// postInit is run after /var/mqm is set up
|
||||
// This version of postInit is only included as part of the MQ Advanced for Developers build
|
||||
func postInit(name string) error {
|
||||
disable := os.Getenv("MQ_DISABLE_WEB_CONSOLE")
|
||||
if disable != "true" && disable != "1" {
|
||||
// Configure the web server (if installed)
|
||||
err := configureWebServer()
|
||||
func postInit(name, keyLabel string, p12Truststore tls.KeyStoreData) error {
|
||||
enableWebServer := os.Getenv("MQ_ENABLE_EMBEDDED_WEB_SERVER")
|
||||
if enableWebServer == "true" || enableWebServer == "1" {
|
||||
// Configure the web server (if enabled)
|
||||
webKeystore, err := configureWebServer(keyLabel, p12Truststore)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// If trust-store is empty, set reference to point to the keystore
|
||||
webTruststoreRef := "MQWebTrustStore"
|
||||
if len(p12Truststore.TrustedCerts) == 0 {
|
||||
webTruststoreRef = "MQWebKeyStore"
|
||||
}
|
||||
// Start the web server, in the background (if installed)
|
||||
// WARNING: No error handling or health checking available for the web server,
|
||||
// which is why it's limited to use with MQ Advanced for Developers only
|
||||
// WARNING: No error handling or health checking available for the web server
|
||||
go func() {
|
||||
startWebServer()
|
||||
err = startWebServer(webKeystore, p12Truststore.Password, webTruststoreRef)
|
||||
if err != nil {
|
||||
log.Printf("Error starting web server: %v", err)
|
||||
}
|
||||
}()
|
||||
}
|
||||
return nil
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
© Copyright IBM Corporation 2017, 2019
|
||||
© Copyright IBM Corporation 2017, 2020
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -16,43 +16,82 @@ limitations under the License.
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/ibm-messaging/mq-container/internal/command"
|
||||
containerruntime "github.com/ibm-messaging/mq-container/internal/containerruntime"
|
||||
"github.com/ibm-messaging/mq-container/internal/mqscredact"
|
||||
"github.com/ibm-messaging/mq-container/internal/mqversion"
|
||||
"github.com/ibm-messaging/mq-container/internal/ready"
|
||||
)
|
||||
|
||||
// createDirStructure creates the default MQ directory structure under /var/mqm
|
||||
func createDirStructure() error {
|
||||
out, _, err := command.Run("/opt/mqm/bin/crtmqdir", "-f", "-s")
|
||||
// log file diagnostics before and after crtmqdir if DEBUG=true
|
||||
logDiagnostics()
|
||||
out, rc, err := command.Run("/opt/mqm/bin/crtmqdir", "-f", "-a")
|
||||
if err != nil {
|
||||
log.Printf("Error creating directory structure: %v\n", string(out))
|
||||
return err
|
||||
if rc == 10 {
|
||||
log.Printf("Warning creating directory structure: %v\n", string(out))
|
||||
} else {
|
||||
log.Printf("Error creating directory structure: %v\n", string(out))
|
||||
return err
|
||||
}
|
||||
}
|
||||
log.Println("Created directory structure under /var/mqm")
|
||||
logDiagnostics()
|
||||
return nil
|
||||
}
|
||||
|
||||
// createQueueManager creates a queue manager, if it doesn't already exist.
|
||||
// It returns true if one was created, or false if one already existed
|
||||
func createQueueManager(name string) (bool, error) {
|
||||
// It returns true if one was created (or a standby was created), or false if one already existed
|
||||
func createQueueManager(name string, devMode bool) (bool, error) {
|
||||
log.Printf("Creating queue manager %v", name)
|
||||
out, rc, err := command.Run("crtmqm", "-q", "-p", "1414", name)
|
||||
|
||||
// Run 'dspmqinf' to check if 'mqs.ini' configuration file exists
|
||||
// If command succeeds, the queue manager (or standby queue manager) has already been created
|
||||
_, _, err := command.Run("dspmqinf", name)
|
||||
if err == nil {
|
||||
log.Printf("Detected existing queue manager %v", name)
|
||||
return false, nil
|
||||
}
|
||||
|
||||
mounts, err := containerruntime.GetMounts()
|
||||
if err != nil {
|
||||
// 8=Queue manager exists, which is fine
|
||||
if rc == 8 {
|
||||
log.Printf("Detected existing queue manager %v", name)
|
||||
return false, nil
|
||||
}
|
||||
log.Printf("crtmqm returned %v", rc)
|
||||
log.Println(string(out))
|
||||
log.Printf("Error getting mounts for queue manager")
|
||||
return false, err
|
||||
}
|
||||
|
||||
// Check if 'qm.ini' configuration file exists for the queue manager
|
||||
// TODO : handle possible race condition - use a file lock?
|
||||
dataDir := getQueueManagerDataDir(mounts, name)
|
||||
_, err = os.Stat(filepath.Join(dataDir, "qm.ini"))
|
||||
if err != nil {
|
||||
// If 'qm.ini' is not found - run 'crtmqm' to create a new queue manager
|
||||
args := getCreateQueueManagerArgs(mounts, name, devMode)
|
||||
out, rc, err := command.Run("crtmqm", args...)
|
||||
if err != nil {
|
||||
log.Printf("Error %v creating queue manager: %v", rc, string(out))
|
||||
return false, err
|
||||
}
|
||||
} else {
|
||||
// If 'qm.ini' is found - run 'addmqinf' to create a standby queue manager with existing configuration
|
||||
args := getCreateStandbyQueueManagerArgs(name)
|
||||
out, rc, err := command.Run("addmqinf", args...)
|
||||
if err != nil {
|
||||
log.Printf("Error %v creating standby queue manager: %v", rc, string(out))
|
||||
return false, err
|
||||
}
|
||||
log.Println("Created standby queue manager")
|
||||
return true, nil
|
||||
}
|
||||
log.Println("Created queue manager")
|
||||
return true, nil
|
||||
}
|
||||
|
||||
@@ -69,10 +108,19 @@ func updateCommandLevel() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func startQueueManager() error {
|
||||
func startQueueManager(name string) error {
|
||||
log.Println("Starting queue manager")
|
||||
out, rc, err := command.Run("strmqm")
|
||||
out, rc, err := command.Run("strmqm", "-x", name)
|
||||
if err != nil {
|
||||
// 30=standby queue manager started, which is fine
|
||||
// 94=native HA replica started, which is fine
|
||||
if rc == 30 {
|
||||
log.Printf("Started standby queue manager")
|
||||
return nil
|
||||
} else if rc == 94 {
|
||||
log.Printf("Started replica queue manager")
|
||||
return nil
|
||||
}
|
||||
log.Printf("Error %v starting queue manager: %v", rc, string(out))
|
||||
return err
|
||||
}
|
||||
@@ -80,67 +128,170 @@ func startQueueManager() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func configureQueueManager() error {
|
||||
const configDir string = "/etc/mqm"
|
||||
files, err := ioutil.ReadDir(configDir)
|
||||
func stopQueueManager(name string) error {
|
||||
log.Println("Stopping queue manager")
|
||||
qmGracePeriod := os.Getenv("MQ_GRACE_PERIOD")
|
||||
isStandby, err := ready.IsRunningAsStandbyQM(name)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
log.Printf("Error getting status for queue manager %v: %v", name, err.Error())
|
||||
return err
|
||||
}
|
||||
for _, file := range files {
|
||||
if strings.HasSuffix(file.Name(), ".mqsc") {
|
||||
abs := filepath.Join(configDir, file.Name())
|
||||
// #nosec G204
|
||||
cmd := exec.Command("runmqsc")
|
||||
// Read mqsc file into variable
|
||||
mqsc, err := ioutil.ReadFile(abs)
|
||||
if err != nil {
|
||||
log.Printf("Error reading file %v: %v", abs, err)
|
||||
continue
|
||||
}
|
||||
// Write mqsc to buffer
|
||||
var buffer bytes.Buffer
|
||||
_, err = buffer.Write(mqsc)
|
||||
if err != nil {
|
||||
log.Printf("Error writing MQSC file %v to buffer: %v", abs, err)
|
||||
continue
|
||||
}
|
||||
// Buffer mqsc to stdin of runmqsc
|
||||
cmd.Stdin = &buffer
|
||||
// Run runmqsc command
|
||||
out, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
log.Errorf("Error running MQSC file %v (%v):\n\t%v", file.Name(), err, formatMQSCOutput(string(out)))
|
||||
continue
|
||||
} else {
|
||||
// Print the runmqsc output, adding tab characters to make it more readable as part of the log
|
||||
log.Printf("Output for \"runmqsc\" with %v:\n\t%v", abs, formatMQSCOutput(string(out)))
|
||||
}
|
||||
args := []string{"-w", "-r", "-tp", qmGracePeriod, name}
|
||||
if os.Getenv("MQ_MULTI_INSTANCE") == "true" {
|
||||
if isStandby {
|
||||
args = []string{"-x", name}
|
||||
} else {
|
||||
args = []string{"-s", "-w", "-r", "-tp", qmGracePeriod, name}
|
||||
}
|
||||
}
|
||||
out, rc, err := command.Run("endmqm", args...)
|
||||
if err != nil {
|
||||
log.Printf("Error %v stopping queue manager: %v", rc, string(out))
|
||||
return err
|
||||
}
|
||||
if isStandby {
|
||||
log.Printf("Stopped standby queue manager")
|
||||
} else {
|
||||
log.Println("Stopped queue manager")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func stopQueueManager(name string) error {
|
||||
log.Println("Stopping queue manager")
|
||||
out, _, err := command.Run("endmqm", "-w", "-r", name)
|
||||
func startMQTrace() error {
|
||||
log.Println("Starting MQ trace")
|
||||
out, rc, err := command.Run("strmqtrc")
|
||||
if err != nil {
|
||||
log.Printf("Error stopping queue manager: %v", string(out))
|
||||
log.Printf("Error %v starting trace: %v", rc, string(out))
|
||||
return err
|
||||
}
|
||||
log.Println("Stopped queue manager")
|
||||
log.Println("Started MQ trace")
|
||||
return nil
|
||||
}
|
||||
|
||||
func endMQTrace() error {
|
||||
log.Println("Ending MQ Trace")
|
||||
out, rc, err := command.Run("endmqtrc")
|
||||
if err != nil {
|
||||
log.Printf("Error %v ending trace: %v", rc, string(out))
|
||||
return err
|
||||
}
|
||||
log.Println("Ended MQ trace")
|
||||
return nil
|
||||
}
|
||||
|
||||
func formatMQSCOutput(out string) string {
|
||||
// redact sensitive information
|
||||
pattern, _ := regexp.Compile("(?i)LDAPPWD\\s*?\\((.*?)\\)")
|
||||
out = pattern.ReplaceAllString(out, "LDAPPWD(*********)")
|
||||
pattern, _ = regexp.Compile("(?i)PASSWORD\\s*?\\((.*?)\\)")
|
||||
out = pattern.ReplaceAllString(out, "PASSWORD(*********)")
|
||||
pattern, _ = regexp.Compile("(?i)SSLCRYP\\s*?\\((.*?)\\)")
|
||||
out = pattern.ReplaceAllString(out, "SSLCRYP(*********)")
|
||||
out, _ = mqscredact.Redact(out)
|
||||
|
||||
// add tab characters to make it more readable as part of the log
|
||||
return strings.Replace(string(out), "\n", "\n\t", -1)
|
||||
}
|
||||
|
||||
func isStandbyQueueManager(name string) (bool, error) {
|
||||
out, rc, err := command.Run("dspmq", "-n", "-m", name)
|
||||
if err != nil {
|
||||
log.Printf("Error %v getting status for queue manager %v: %v", rc, name, string(out))
|
||||
return false, err
|
||||
}
|
||||
if strings.Contains(string(out), "(RUNNING AS STANDBY)") {
|
||||
return true, nil
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func getQueueManagerDataDir(mounts map[string]string, name string) string {
|
||||
dataDir := filepath.Join("/var/mqm/qmgrs", name)
|
||||
if _, ok := mounts["/mnt/mqm-data"]; ok {
|
||||
dataDir = filepath.Join("/mnt/mqm-data/qmgrs", name)
|
||||
}
|
||||
return dataDir
|
||||
}
|
||||
|
||||
func getCreateQueueManagerArgs(mounts map[string]string, name string, devMode bool) []string {
|
||||
|
||||
mqversionBase := "9.2.1.0"
|
||||
|
||||
// use "UserExternal" only if we are 9.2.1.0 or above.
|
||||
oaVal := "user"
|
||||
mqVersionCheck, err := mqversion.Compare(mqversionBase)
|
||||
|
||||
if err != nil {
|
||||
log.Printf("Error comparing MQ versions for oa,rc: %v", mqVersionCheck)
|
||||
}
|
||||
if mqVersionCheck >= 0 {
|
||||
oaVal = "UserExternal"
|
||||
}
|
||||
|
||||
//build args
|
||||
args := []string{"-ii", "/etc/mqm/", "-ic", "/etc/mqm/", "-q", "-p", "1414"}
|
||||
|
||||
if os.Getenv("MQ_NATIVE_HA") == "true" {
|
||||
args = append(args, "-lr", os.Getenv("HOSTNAME"))
|
||||
}
|
||||
if devMode {
|
||||
args = append(args, "-oa", oaVal)
|
||||
}
|
||||
if _, ok := mounts["/mnt/mqm-log"]; ok {
|
||||
args = append(args, "-ld", "/mnt/mqm-log/log")
|
||||
}
|
||||
if _, ok := mounts["/mnt/mqm-data"]; ok {
|
||||
args = append(args, "-md", "/mnt/mqm-data/qmgrs")
|
||||
}
|
||||
args = append(args, name)
|
||||
return args
|
||||
}
|
||||
|
||||
func getCreateStandbyQueueManagerArgs(name string) []string {
|
||||
args := []string{"-s", "QueueManager"}
|
||||
args = append(args, "-v", fmt.Sprintf("Name=%v", name))
|
||||
args = append(args, "-v", fmt.Sprintf("Directory=%v", name))
|
||||
args = append(args, "-v", "Prefix=/var/mqm")
|
||||
args = append(args, "-v", fmt.Sprintf("DataPath=/mnt/mqm-data/qmgrs/%v", name))
|
||||
return args
|
||||
}
|
||||
|
||||
// updateQMini removes the original ServicecCmponent stanza so we can add a new one
|
||||
func updateQMini(qmname string) error {
|
||||
|
||||
val, set := os.LookupEnv("MQ_CONNAUTH_USE_HTP")
|
||||
if !set {
|
||||
//htpasswd mode not enabled.
|
||||
return nil
|
||||
}
|
||||
bval, err := strconv.ParseBool(strings.ToLower(val))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if bval == false {
|
||||
//htpasswd mode not enabled.
|
||||
return nil
|
||||
}
|
||||
|
||||
log.Printf("Removing existing ServiceComponent configuration")
|
||||
|
||||
mounts, err := containerruntime.GetMounts()
|
||||
if err != nil {
|
||||
log.Printf("Error getting mounts for queue manager")
|
||||
return err
|
||||
}
|
||||
dataDir := getQueueManagerDataDir(mounts, qmname)
|
||||
qmgrDir := filepath.Join(dataDir, "qm.ini")
|
||||
//read the initial version.
|
||||
// #nosec G304 - qmgrDir filepath is derived from dspmqinf
|
||||
iniFileBytes, err := ioutil.ReadFile(qmgrDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
qminiConfigStr := string(iniFileBytes)
|
||||
if strings.Contains(qminiConfigStr, "ServiceComponent:") {
|
||||
var re = regexp.MustCompile(`(?m)^.*ServiceComponent.*$\s^.*Service.*$\s^.*Name.*$\s^.*Module.*$\s^.*ComponentDataSize.*$`)
|
||||
curFile := re.ReplaceAllString(qminiConfigStr, "")
|
||||
// #nosec G304 G306 - qmgrDir filepath is derived from dspmqinf and
|
||||
// its a read by owner/s group, and pose no harm.
|
||||
err := ioutil.WriteFile(qmgrDir, []byte(curFile), 0660)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
© Copyright IBM Corporation 2017, 2018
|
||||
© Copyright IBM Corporation 2017, 2022
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -33,8 +33,8 @@ func signalHandler(qmgr string) chan int {
|
||||
control := make(chan int)
|
||||
// Use separate channels for the signals, to avoid SIGCHLD signals swamping
|
||||
// the buffer, and preventing other signals.
|
||||
stopSignals := make(chan os.Signal)
|
||||
reapSignals := make(chan os.Signal)
|
||||
stopSignals := make(chan os.Signal, 1)
|
||||
reapSignals := make(chan os.Signal, 1)
|
||||
signal.Notify(stopSignals, syscall.SIGTERM, syscall.SIGINT)
|
||||
go func() {
|
||||
for {
|
||||
|
||||
@@ -20,6 +20,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/ibm-messaging/mq-container/internal/command"
|
||||
"github.com/ibm-messaging/mq-container/internal/mqversion"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -38,11 +39,11 @@ func logDateStamp() {
|
||||
}
|
||||
|
||||
func logGitRepo() {
|
||||
log.Printf("Image revision: %v", ImageRevision)
|
||||
// log.Printf("Image revision: %v", ImageRevision)
|
||||
}
|
||||
|
||||
func logGitCommit() {
|
||||
log.Printf("Image source: %v", ImageSource)
|
||||
// log.Printf("Image source: %v", ImageSource)
|
||||
}
|
||||
|
||||
func logImageTag() {
|
||||
@@ -50,7 +51,7 @@ func logImageTag() {
|
||||
}
|
||||
|
||||
func logMQVersion() {
|
||||
mqVersion, _, err := command.Run("dspmqver", "-b", "-f", "2")
|
||||
mqVersion, err := mqversion.Get()
|
||||
if err != nil {
|
||||
log.Printf("Error Getting MQ version: %v", strings.TrimSuffix(string(mqVersion), "\n"))
|
||||
}
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
// +build mqdev
|
||||
|
||||
/*
|
||||
© Copyright IBM Corporation 2018, 2019
|
||||
© Copyright IBM Corporation 2018, 2020
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -19,49 +17,42 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"os/user"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"syscall"
|
||||
"strings"
|
||||
|
||||
"github.com/ibm-messaging/mq-container/internal/command"
|
||||
"github.com/ibm-messaging/mq-container/internal/copy"
|
||||
"github.com/ibm-messaging/mq-container/internal/mqtemplate"
|
||||
"github.com/ibm-messaging/mq-container/internal/tls"
|
||||
)
|
||||
|
||||
func startWebServer() error {
|
||||
func startWebServer(webKeystore, webkeystorePW, webTruststoreRef string) error {
|
||||
_, err := os.Stat("/opt/mqm/bin/strmqweb")
|
||||
if err != nil && os.IsNotExist(err) {
|
||||
log.Debug("Skipping web server, because it's not installed")
|
||||
return nil
|
||||
}
|
||||
log.Println("Starting web server")
|
||||
// #nosec G204 - command is fixed, no injection vector
|
||||
cmd := exec.Command("strmqweb")
|
||||
// Set a default app password for the web server, if one isn't already set
|
||||
_, set := os.LookupEnv("MQ_APP_PASSWORD")
|
||||
if !set {
|
||||
// Take all current environment variables, and add the app password
|
||||
cmd.Env = append(os.Environ(), "MQ_APP_PASSWORD=passw0rd")
|
||||
} else {
|
||||
cmd.Env = os.Environ()
|
||||
}
|
||||
uid, gid, err := command.LookupMQM()
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
// TLS enabled
|
||||
if webKeystore != "" {
|
||||
cmd.Env = append(cmd.Env, "AMQ_WEBKEYSTORE="+webKeystore)
|
||||
cmd.Env = append(cmd.Env, "AMQ_WEBKEYSTOREPW="+webkeystorePW)
|
||||
cmd.Env = append(cmd.Env, "AMQ_WEBTRUSTSTOREREF="+webTruststoreRef)
|
||||
}
|
||||
u, err := user.Current()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
currentUID, err := strconv.Atoi(u.Uid)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error converting UID to string: %v", err)
|
||||
}
|
||||
// Add credentials to run as 'mqm', only if we aren't already 'mqm'
|
||||
if currentUID != uid {
|
||||
cmd.SysProcAttr = &syscall.SysProcAttr{}
|
||||
cmd.SysProcAttr.Credential = &syscall.Credential{Uid: uint32(uid), Gid: uint32(gid)}
|
||||
}
|
||||
out, rc, err := command.RunCmd(cmd)
|
||||
out, err := cmd.CombinedOutput()
|
||||
rc := cmd.ProcessState.ExitCode()
|
||||
if err != nil {
|
||||
log.Printf("Error %v starting web server: %v", rc, string(out))
|
||||
return err
|
||||
@@ -70,45 +61,100 @@ func startWebServer() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// CopyFile copies the specified file
|
||||
func CopyFile(src, dest string) error {
|
||||
log.Debugf("Copying file %v to %v", src, dest)
|
||||
in, err := os.Open(src)
|
||||
if err != nil {
|
||||
return err
|
||||
func configureSSO(p12TrustStore tls.KeyStoreData, webKeystore string) (string, error) {
|
||||
requiredEnvVars := []string{}
|
||||
_, set := os.LookupEnv("MQ_ZEN_INTERNAL_ENDPOINT")
|
||||
if !set {
|
||||
// Ensure all required environment variables are set for SSO
|
||||
requiredEnvVars = []string{
|
||||
"MQ_OIDC_CLIENT_ID",
|
||||
"MQ_OIDC_CLIENT_SECRET",
|
||||
"MQ_OIDC_UNIQUE_USER_IDENTIFIER",
|
||||
"MQ_OIDC_AUTHORIZATION_ENDPOINT",
|
||||
"MQ_OIDC_TOKEN_ENDPOINT",
|
||||
"MQ_OIDC_JWK_ENDPOINT",
|
||||
"MQ_OIDC_ISSUER_IDENTIFIER",
|
||||
}
|
||||
} else {
|
||||
// Ensure all required environment variables are set for Zen SSO
|
||||
requiredEnvVars = []string{
|
||||
"MQ_ZEN_UNIQUE_USER_IDENTIFIER",
|
||||
"MQ_ZEN_INTERNAL_ENDPOINT",
|
||||
"MQ_ZEN_ISSUER_IDENTIFIER",
|
||||
"MQ_ZEN_AUDIENCES",
|
||||
"MQ_ZEN_CONTEXT_NAME",
|
||||
"MQ_ZEN_BASE_URI",
|
||||
"MQ_ZEN_CONTEXT_NAMESPACE",
|
||||
"IAM_URL",
|
||||
}
|
||||
}
|
||||
defer in.Close()
|
||||
|
||||
out, err := os.OpenFile(dest, os.O_CREATE|os.O_WRONLY, 0770)
|
||||
defer out.Close()
|
||||
|
||||
_, err = io.Copy(out, in)
|
||||
if err != nil {
|
||||
return err
|
||||
for _, envVar := range requiredEnvVars {
|
||||
if len(os.Getenv(envVar)) == 0 {
|
||||
return "", fmt.Errorf("%v must be set when MQ_BETA_ENABLE_SSO=true", envVar)
|
||||
}
|
||||
}
|
||||
err = out.Close()
|
||||
return err
|
||||
}
|
||||
|
||||
func configureWebServer() error {
|
||||
_, err := os.Stat("/opt/mqm/bin/strmqweb")
|
||||
// Check mqweb directory exists
|
||||
const mqwebDir string = "/etc/mqm/web/installations/Installation1/servers/mqweb"
|
||||
_, err := os.Stat(mqwebDir)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return nil
|
||||
return "", nil
|
||||
}
|
||||
return err
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Process SSO template for generating file mqwebuser.xml
|
||||
adminUsers := strings.Split(os.Getenv("MQ_WEB_ADMIN_USERS"), "\n")
|
||||
err = mqtemplate.ProcessTemplateFile(mqwebDir+"/mqwebuser.xml.tpl", mqwebDir+"/mqwebuser.xml", map[string][]string{"AdminUser": adminUsers}, log)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Configure SSO TLS
|
||||
return tls.ConfigureWebKeystore(p12TrustStore, webKeystore)
|
||||
}
|
||||
|
||||
func configureWebServer(keyLabel string, p12Truststore tls.KeyStoreData) (string, error) {
|
||||
var webKeystore string
|
||||
|
||||
// Configure TLS for Web Console first if we have a certificate to use
|
||||
err := tls.ConfigureWebTLS(keyLabel)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if keyLabel != "" {
|
||||
webKeystore = keyLabel + ".p12"
|
||||
}
|
||||
|
||||
// Configure Single-Sign-On for the web server (if enabled)
|
||||
enableSSO := os.Getenv("MQ_BETA_ENABLE_SSO")
|
||||
if enableSSO == "true" || enableSSO == "1" {
|
||||
webKeystore, err = configureSSO(p12Truststore, webKeystore)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
} else if keyLabel == "" && os.Getenv("MQ_GENERATE_CERTIFICATE_HOSTNAME") != "" {
|
||||
webKeystore, err = tls.ConfigureWebKeystore(p12Truststore, webKeystore)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
|
||||
_, err = os.Stat("/opt/mqm/bin/strmqweb")
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return "", nil
|
||||
}
|
||||
return "", err
|
||||
}
|
||||
const webConfigDir string = "/etc/mqm/web"
|
||||
_, err = os.Stat(webConfigDir)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return nil
|
||||
return "", nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
uid, gid, err := command.LookupMQM()
|
||||
if err != nil {
|
||||
return err
|
||||
return "", err
|
||||
}
|
||||
const prefix string = "/etc/mqm/web"
|
||||
err = filepath.Walk(prefix, func(from string, info os.FileInfo, err error) error {
|
||||
@@ -127,6 +173,7 @@ func configureWebServer() error {
|
||||
}
|
||||
if info.IsDir() {
|
||||
if !exists {
|
||||
// #nosec G301 - write group permissions are required
|
||||
err := os.MkdirAll(to, 0770)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -139,17 +186,14 @@ func configureWebServer() error {
|
||||
return err
|
||||
}
|
||||
}
|
||||
err := CopyFile(from, to)
|
||||
err := copy.CopyFile(from, to)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
err = os.Chown(to, uid, gid)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
return err
|
||||
|
||||
return webKeystore, err
|
||||
}
|
||||
|
||||
6
config.env
Normal file
6
config.env
Normal file
@@ -0,0 +1,6 @@
|
||||
###########################################################################################################################################################
|
||||
|
||||
# MQ_VERSION is the fully qualified MQ version number to build
|
||||
MQ_VERSION ?= 9.3.0.5
|
||||
|
||||
###########################################################################################################################################################
|
||||
@@ -1,4 +1,4 @@
|
||||
# © Copyright IBM Corporation 2015, 2017
|
||||
# © Copyright IBM Corporation 2020
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
@@ -12,7 +12,6 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
FROM mqadvanced
|
||||
RUN useradd alice -G mqm && \
|
||||
echo alice:passw0rd | chpasswd
|
||||
COPY *.mqsc /etc/mqm/
|
||||
FROM fedora:32
|
||||
RUN yum install skopeo -y -qq
|
||||
ENTRYPOINT [ "skopeo" ]
|
||||
@@ -2,46 +2,27 @@
|
||||
|
||||
## Prerequisites
|
||||
|
||||
### Prerequisites for building an Ubuntu image
|
||||
If you want to build a container image with Ubuntu Linux as the base OS, then you need to have the following tools installed:
|
||||
You need to have the following tools installed:
|
||||
|
||||
* [Docker](https://www.docker.com/) V17.06.1 or later
|
||||
* [Docker](https://www.docker.com/) 17.06.1 or later, or [Podman](https://podman.io) 1.0 or later (Podman 4.1 on macOS). If using Podman on macOS, the you need to be in "rootful" mode to allow the use of a network during builds. Run `podman machine init --rootful`.
|
||||
* [GNU make](https://www.gnu.org/software/make/)
|
||||
|
||||
If you are working in the Windows Subsystem for Linux, follow [this guide by Microsoft to set up Docker](https://blogs.msdn.microsoft.com/commandline/2017/12/08/cross-post-wsl-interoperability-with-docker/) first.
|
||||
|
||||
### Prerequisites for building a Red Hat Enterprise Linux image
|
||||
If you want to build a container image with Red Hat Enterprise Linux as the base OS, then you need to use a host server with Red Hat Enterprise Linux. You must also have the following tools installed:
|
||||
|
||||
* [`buildah`](https://buildah.io) (available in `rhel-7-server-extras`)
|
||||
* [`podman`](https://podman.io) (available in `rhel-7-server-extras`)
|
||||
|
||||
In addition, you need the following commonly installed tools:
|
||||
|
||||
* `bash`
|
||||
* `coreutils`
|
||||
* `findutils`
|
||||
* `make`
|
||||
* `sed`
|
||||
* `shadow-utils`
|
||||
* `tar`
|
||||
|
||||
## Building a production image
|
||||
This procedure works for building the MQ Continuous Delivery release, on `x86_64`, `ppc64le` and `s390x` architectures.
|
||||
|
||||
From MQ 9.2.X, the MQ container adds support for MQ Long Term Support (LTS) **production licensed** releases.
|
||||
|
||||
### Building MQ 9.3 Long Term Support (LTS) and Continuous Delivery (CD)
|
||||
|
||||
**Note**: MQ 9.3 is the latest MQ version with MQ Long Term Support (LTS), as well as being the latest Continuous Delivery (CD) version.
|
||||
|
||||
The procedure below is for building the 9.3 release, on `amd64`, `ppc64le` and `s390x` architectures.
|
||||
|
||||
1. Create a `downloads` directory in the root of this repository
|
||||
2. Download MQ from [IBM Passport Advantage](https://www.ibm.com/software/passportadvantage/) or [IBM Fix Central](https://www.ibm.com/support/fixcentral), and place the downloaded file (for example, `IBM_MQ_9.1.1_UBUNTU_X86-64.tar.gz` for MQ V9.1.1 for Ubuntu on x86_64 architecture) in the `downloads` directory
|
||||
3. Run `make build-advancedserver`
|
||||
|
||||
> **Warning**: Note that MQ offers two different sets of packaging on Linux: one is called "MQ for Linux" and contains RPM files for installing on Red Hat Enterprise Linux and SUSE Linux Enterprise Server. The other package is called "MQ for Ubuntu", and contains DEB files for installing on Ubuntu.
|
||||
|
||||
On a Red Hat Enterprise Linux host, the command `make build-advancedserver` will build a container image using Red Hat Enterprise Linux as the base. On all other hosts, the base image will be Ubuntu.
|
||||
|
||||
You can build a different version of MQ by setting the `MQ_VERSION` environment variable, for example:
|
||||
|
||||
```bash
|
||||
MQ_VERSION=9.1.0.0 make build-advancedserver
|
||||
```
|
||||
2. Download MQ from [IBM Passport Advantage](https://www.ibm.com/software/passportadvantage/). Identify the correct 'Long Term Support Release for Containers' eImage part number for your architecture from the 9.3.0 LTS tab at https://www.ibm.com/support/pages/downloading-ibm-mq-930
|
||||
3. Ensure the `tar.gz` file is in the `downloads` directory
|
||||
4. Run `make build-advancedserver`
|
||||
|
||||
If you have an MQ archive file with a different file name, you can specify a particular file (which must be in the `downloads` directory). You should also specify the MQ version, so that the resulting image is tagged correctly, for example:
|
||||
|
||||
@@ -49,11 +30,19 @@ If you have an MQ archive file with a different file name, you can specify a par
|
||||
MQ_ARCHIVE=mq-1.2.3.4.tar.gz MQ_VERSION=1.2.3.4 make build-advancedserver
|
||||
```
|
||||
|
||||
### Building previous MQ Long Term Support (LTS)
|
||||
|
||||
**Note**: MQ 9.3 is the latest MQ version with MQ Long Term Support (LTS), as well as being the latest Continuous Delivery (CD) version. Therefore, to build build 9.3.0.X, follow the [instructions above for MQ 9.3](#building-mq-93-long-term-support-lts-and-continuous-delivery-cd).
|
||||
|
||||
However, if you wish to build the previous MQ LTS, use the [instructions](/../9.2.0.x/docs/building.md#mq-long-term-support-lts) in the `v9.2.0.x-eus` branch.
|
||||
|
||||
|
||||
## Building a developer image
|
||||
Run `make build-devserver`, which will download the latest version of MQ Advanced for Developers from IBM developerWorks. This is currently only available on the `x86_64` architecture. On a Red Hat Enterprise Linux host, this command will build a container image using Red Hat Enterprise Linux as the base. On all other hosts, the base image will be Ubuntu.
|
||||
|
||||
Run `make build-devserver`, which will download the latest version of MQ Advanced for Developers from IBM developerWorks. This is currently only available on the `amd64` architecture.
|
||||
|
||||
You can use the environment variable `MQ_ARCHIVE_DEV` to specify an alternative local file to install from (which must be in the `downloads` directory).
|
||||
|
||||
## Installed components
|
||||
|
||||
This image includes the core MQ server, Java, language packs, and GSKit. This can be configured by setting the `MQ_PACKAGES` argument to `make`. For the Ubuntu-based image, you can also directly set a [Docker build argument](https://docs.docker.com/engine/reference/commandline/build/#set-build-time-variables-build-arg).
|
||||
This image includes the core MQ server, Java, language packs, GSKit, and web server. This is configured in the `Generate MQ package in INSTALLATION_DIR` section [here](../install-mq.sh), with the configured options being picked up at build time.
|
||||
|
||||
@@ -9,14 +9,12 @@ The MQ Developer Defaults supports some customization options, these are all con
|
||||
* **MQ_DEV** - Set this to `false` to stop the default objects being created.
|
||||
* **MQ_ADMIN_PASSWORD** - Changes the password of the `admin` user. Must be at least 8 characters long.
|
||||
* **MQ_APP_PASSWORD** - Changes the password of the app user. If set, this will cause the `DEV.APP.SVRCONN` channel to become secured and only allow connections that supply a valid userid and password. Must be at least 8 characters long.
|
||||
* **MQ_TLS_KEYSTORE** - Allows you to supply the location of a PKCS#12 keystore containing a single certificate which you want to use in both the web console and the queue manager. Requires `MQ_TLS_PASSPHRASE`. When enabled the channels created will be secured using the `TLS_RSA_WITH_AES_128_CBC_SHA256` CipherSpec. *Note*: you will need to make the keystore available inside your container, this can be done by mounting a volume to your container.
|
||||
* **MQ_TLS_PASSPHRASE** - Passphrase for the keystore referenced in `MQ_TLS_KEYSTORE`.
|
||||
|
||||
## Details of the default configuration
|
||||
|
||||
The following users are created:
|
||||
|
||||
* User **admin** for administration (in the `mqm` group). Default password is **passw0rd**.
|
||||
* User **admin** for administration. Default password is **passw0rd**.
|
||||
* User **app** for messaging (in a group called `mqclient`). No password by default.
|
||||
|
||||
Users in `mqclient` group have been given access connect to all queues and topics starting with `DEV.**` and have `put`, `get`, `pub`, `sub`, `browse` and `inq` permissions.
|
||||
@@ -34,11 +32,6 @@ Two channels are created, one for administration, the other for normal messaging
|
||||
* DEV.ADMIN.SVRCONN - configured to only allow the `admin` user to connect into it. A user and password must be supplied.
|
||||
* DEV.APP.SVRCONN - does not allow administrative users to connect. Password is optional unless you choose a password for app users.
|
||||
|
||||
A new listener is created (the SYSTEM listener is fine, but system objects are not shown by default in the web console):
|
||||
|
||||
* DEV.LISTENER.TCP - listens on port 1414.
|
||||
|
||||
|
||||
## Web Console
|
||||
|
||||
By default the MQ Advanced for Developers image will start the IBM MQ Web Console that allows you to administer your Queue Manager running on your container. When the web console has been started, you can access it by opening a web browser and navigating to https://<Container IP>:9443/ibmmq/console. Where <Container IP> is replaced by the IP address of your running container.
|
||||
@@ -50,6 +43,6 @@ If you choose to accept the security warning, you will be presented with the log
|
||||
* **User:** admin
|
||||
* **Password:** passw0rd
|
||||
|
||||
If you wish to change the password for the admin user, this can be done using the `MQ_ADMIN_PASSWORD` environment variable. If you supply a PKCS#12 keystore using the `MQ_TLS_KEYSTORE` environment variable, then the web console will be configured to use the certificate inside the keystore for HTTPS operations.
|
||||
If you wish to change the password for the admin user, this can be done using the `MQ_ADMIN_PASSWORD` environment variable.
|
||||
|
||||
If you do not wish the web console to run, you can disable it by setting the environment variable `MQ_DISABLE_WEB_CONSOLE` to `true`.
|
||||
If you do not wish the web console to run, you can disable it by setting the environment variable `MQ_ENABLE_EMBEDDED_WEB_SERVER` to `false`.
|
||||
|
||||
@@ -11,6 +11,7 @@ The resulting Docker image contains the following:
|
||||
- `runmqdevserver` - The main process for MQ Advanced for Developers
|
||||
- `chkmqhealthy` - Checks the health of the queue manager. This can be used by (say) a Kubernetes liveness probe.
|
||||
- `chkmqready` - Checks if the queue manager is ready for work. This can be used by (say) a Kubernetes readiness probe.
|
||||
- `chkmqstarted` - Checks if the queue manager has successfully started. This can be used by (say) a Kubernetes startup probe.
|
||||
|
||||
## runmqserver
|
||||
The `runmqserver` command has the following responsibilities:
|
||||
@@ -24,6 +25,7 @@ The `runmqserver` command has the following responsibilities:
|
||||
- Works as PID 1, so is responsible for [reaping zombie processes](https://blog.phusion.nl/2015/01/20/docker-and-the-pid-1-zombie-reaping-problem/)
|
||||
* Creating and starting a queue manager
|
||||
* Configuring the queue manager, by running any MQSC scripts found under `/etc/mqm`
|
||||
* Starts the MQ web server (if enabled)
|
||||
* Starting Prometheus metrics generation for the queue manager (if enabled)
|
||||
* Indicates to the `chkmqready` command that configuration is complete, and that normal readiness checking can happen. This is done by writing a file into `/run/runmqserver`
|
||||
|
||||
@@ -36,8 +38,6 @@ The `runmqdevserver` command is added to the MQ Advanced for Developers image on
|
||||
2. Generates MQSC files to put in `/etc/mqm`, based on a template, which is updated with values based on supplied environment variables.
|
||||
3. If requested, it creates TLS key stores under `/run/runmqdevserver`, and configures MQ and the web server to use them
|
||||
|
||||
A special version of `runmqserver` is used in the developer image, which performs extra actions like starting the web server. This is built using the `mqdev` [build constraint](https://golang.org/pkg/go/build/#hdr-Build_Constraints).
|
||||
|
||||
## Prometheus metrics
|
||||
[Prometheus](https://prometheus.io) metrics are generated for the queue manager as follows:
|
||||
|
||||
|
||||
29
docs/pluggable-connauth.md
Normal file
29
docs/pluggable-connauth.md
Normal file
@@ -0,0 +1,29 @@
|
||||
|
||||
### Queue Manager Connection Authentication using a htpasswd file
|
||||
|
||||
This pluggable authentication mode is to allow developers using the mq-container developer image to define users and their credentials into a .htpasswd file. This is in addition to the existing methods of MQ Connection Authentication (`CONNAUTH`) using Operating System or LDAP users.
|
||||
|
||||
**Please note:**
|
||||
1. This new feature is enabled only when environment variable `--env MQ_CONNAUTH_USE_HTP=true` is set while creating a container.
|
||||
2. When enabled, the `AuthType` value of the ConnectionAuthentication (`CONNAUTH`) is ignored and htpasswd mode is used. However, the MQ authority records created using (`SETMQAUT` or `AUTHREC`) will be in effect while using the htpasswd mode.
|
||||
3. Channel Authentication records (`CHLAUTH`) will be in effect while using the htpasswd mode.
|
||||
4. Passwords should be encrypted using bcrypt (golang.org/x/crypto/bcrypt).
|
||||
5. This is developer only feature and not recommended for use in Production.
|
||||
|
||||
### Preparing htpasswd file
|
||||
|
||||
1. A default `mq.htpasswd` file is provided and placed under /etc/mqm/ directory inside the container.
|
||||
2. You can set the password for user `admin` by setting the environment variable `MQ_ADMIN_PASSWORD`.
|
||||
3. You can add user `app` into mq.htpasswd file by setting the environment variable `MQ_APP_PASSWORD`. This user `app` can be used to access `DEV.*` objects of the queue manager.
|
||||
|
||||
#### Next Steps:
|
||||
|
||||
Use an administrative tool or your application to connect to queue manager using the credentials defined in the mq.htpasswd file.
|
||||
|
||||
**Please note**: When an authentication request is made with a userid that is not defined in the `mq.htpasswd` file, then the authentication process is delegated to queue manager to handle. This will then use `IDPWOS` or `LDAP` modes for further processing.
|
||||
|
||||
#### Troubleshooting
|
||||
|
||||
A log file named `mqhtpass.log` is generated under `/var/mqm/errors` directory path of the container. This file will contain all the failed connection authentication requests. Additional information is logged to this file if the environment variable `DEBUG` is set to `true`.
|
||||
|
||||
**Please note**: This log file will be wiped when the queue manager is next started.
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
### User
|
||||
|
||||
The MQ server image is run using the "mqm" user. On the Ubuntu-based image, this uses the UID and GID of 999. On the Red Hat Enterprise Linux image, it uses the UID and GID of 888.
|
||||
The MQ server image is run using with UID 1001, though this can be any UID, with a fixed GID of 0 (root).
|
||||
|
||||
### Capabilities
|
||||
|
||||
@@ -16,24 +16,5 @@ docker run \
|
||||
--env LICENSE=accept \
|
||||
--env MQ_QMGR_NAME=QM1 \
|
||||
--detach \
|
||||
mqadvanced-server:9.1.2.0-x86_64-ubuntu-16.04
|
||||
ibm-mqadvanced-server:9.3.0.5-amd64
|
||||
```
|
||||
|
||||
The MQ Advanced for Developers image does require the "chown", "setuid", "setgid" and "audit_write" capabilities (plus "dac_override" if you're using an image based on Red Hat Enterprise Linux). This is because it uses the "sudo" command to change passwords inside the container. For example, in Docker, you could do the following:
|
||||
|
||||
```sh
|
||||
docker run \
|
||||
--cap-drop=ALL \
|
||||
--cap-add=CHOWN \
|
||||
--cap-add=SETUID \
|
||||
--cap-add=SETGID \
|
||||
--cap-add=AUDIT_WRITE \
|
||||
--env LICENSE=accept \
|
||||
--env MQ_QMGR_NAME=QM1 \
|
||||
--detach \
|
||||
mqadvanced-server-dev:9.1.2.0-x86_64-ubuntu-16.04
|
||||
```
|
||||
|
||||
### SELinux
|
||||
|
||||
The SELinux label "spc_t" (super-privileged container) is needed to run the MQ container on a host with SELinux enabled. This is due to a current limitation in how MQ data is stored on volumes, which violates the usual policy applied when using the standard "container_t" label.
|
||||
@@ -2,17 +2,9 @@
|
||||
|
||||
## Prerequisites
|
||||
You need to ensure you have the following tools installed:
|
||||
* [Docker](https://www.docker.com/)
|
||||
* [Docker](https://www.docker.com/) 19.03 or higher (API version 1.40)
|
||||
* [GNU make](https://www.gnu.org/software/make/)
|
||||
* [Go](https://golang.org/) - only needed for running the tests
|
||||
* [dep](https://github.com/golang/dep) (official Go dependency management tool) - needed to prepare for running the tests
|
||||
* [Helm](https://helm.sh) - only needed for running the Kubernetes tests
|
||||
|
||||
### Prerequisites for testing a RedHat image
|
||||
If you want to test a container image with Red Hat Enterprise Linux as the base OS, then you need to use a host server with Red Hat Enterprise Linux. You must also have the following tools installed:
|
||||
|
||||
* [Yum](http://yum.baseurl.org/) (available in `rhel-7-server-extras`)
|
||||
* [Buildah](https://buildah.io) (available in `rhel-7-server-extras`)
|
||||
|
||||
## Running the tests
|
||||
There are two main sets of tests:
|
||||
@@ -21,29 +13,30 @@ There are two main sets of tests:
|
||||
2. Docker tests, which test a complete Docker image, using the Docker API
|
||||
|
||||
### Running the Docker tests
|
||||
The Docker tests can be run locally on a machine with Docker. For example:
|
||||
|
||||
The Docker tests can be run locally on a machine with Docker. For example:
|
||||
|
||||
```
|
||||
make test-devserver
|
||||
make test-advancedserver
|
||||
make devserver
|
||||
make advancedserver
|
||||
```
|
||||
|
||||
You can specify the image to use directly by using the `MQ_IMAGE_ADVANCEDSERVER` or `MQ_IMAGE_DEVSERVER` variables, for example:
|
||||
|
||||
```
|
||||
MQ_IMAGE_ADVANCEDSERVER=mqadvanced-server:9.1.2.0-x86_64-ubuntu-16.04 make test-advancedserver
|
||||
MQ_IMAGE_ADVANCEDSERVER=ibm-mqadvanced-server:9.3.0.5-amd64 make test-advancedserver
|
||||
```
|
||||
|
||||
You can pass parameters to `go test` with an environment variable. For example, to run the "TestGoldenPath" test, run the following command::
|
||||
You can pass parameters to `go test` with an environment variable. For example, to run the "TestGoldenPath" test, run the following command:
|
||||
|
||||
```
|
||||
TEST_OPTS_DOCKER="-run TestGoldenPath" make test-advancedserver
|
||||
```
|
||||
|
||||
You can also use the same environment variables you specified when [building](./building), for example, the following will try and test an image called `mqadvanced-server:9.1.2.0-x86_64-ubuntu-16.04`:
|
||||
You can also use the same environment variables you specified when [building](./building), for example, the following will try and test an image called `ibm-mqadvanced-server:9.2.0.0-amd64`:
|
||||
|
||||
```
|
||||
MQ_VERSION=9.1.2.0 make test-advancedserver
|
||||
MQ_VERSION=9.2.0.0 make test-advancedserver
|
||||
```
|
||||
|
||||
### Running the Docker tests with code coverage
|
||||
@@ -55,12 +48,3 @@ make test-advancedserver-cover
|
||||
```
|
||||
|
||||
In order to generate code coverage metrics from the Docker tests, the build step creates a new Docker image with an instrumented version of the code. Each test is then run individually, producing a coverage report each under `test/docker/coverage/`. These individual reports are then combined. The combined report is written to the `coverage` directory.
|
||||
|
||||
|
||||
### Running the Kubernetes tests
|
||||
|
||||
For the Kubernetes tests, you need to have built the Docker image, and pushed it to the registry used by your Kubernetes cluster. Most of the configuration used by the tests is picked up from your `kubectl` configuration, but you will typically need to specify the image details. For example:
|
||||
|
||||
```bash
|
||||
MQ_IMAGE=mycluster.icp:8500/default/mq-devserver make test-kubernetes-devserver
|
||||
```
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
In order to use the image, it is necessary to accept the terms of the IBM MQ license. This is achieved by specifying the environment variable `LICENSE` equal to `accept` when running the image. You can also view the license terms by setting this variable to `view`. Failure to set the variable will result in the termination of the container with a usage statement. You can view the license in a different language by also setting the `LANG` environment variable.
|
||||
|
||||
> **Note**: You can use `podman` instead of `docker` in any of the examples on this page.
|
||||
|
||||
## Running with the default configuration
|
||||
You can run a queue manager with the default configuration and a listener on port 1414 using the following command. For example, the following command creates and starts a queue manager called `QM1`, and maps port 1414 on the host to the MQ listener on port 1414 inside the container, as well as port 9443 on the host to the web console on port 9443 inside the container:
|
||||
|
||||
@@ -12,7 +14,7 @@ docker run \
|
||||
--publish 1414:1414 \
|
||||
--publish 9443:9443 \
|
||||
--detach \
|
||||
ibmcom/mq
|
||||
icr.io/ibm-messaging/mq
|
||||
```
|
||||
|
||||
## Running with the default configuration and a volume
|
||||
@@ -32,7 +34,7 @@ docker run \
|
||||
--publish 9443:9443 \
|
||||
--detach \
|
||||
--volume qm1data:/mnt/mqm \
|
||||
ibmcom/mq
|
||||
icr.io/ibm-messaging/mq
|
||||
```
|
||||
|
||||
The Docker image always uses `/mnt/mqm` for MQ data, which is correctly linked for you under `/var/mqm` at runtime. This is to handle problems with file permissions on some platforms.
|
||||
@@ -49,7 +51,7 @@ docker run \
|
||||
--publish 9443:9443 \
|
||||
--publish 9157:9157 \
|
||||
--detach \
|
||||
ibmcom/mq
|
||||
icr.io/ibm-messaging/mq
|
||||
```
|
||||
|
||||
## Customizing the queue manager configuration
|
||||
@@ -58,23 +60,18 @@ You can customize the configuration in several ways:
|
||||
|
||||
1. For getting started, you can use the [default developer configuration](developer-config.md), which is available out-of-the-box for the MQ Advanced for Developers image
|
||||
2. By creating your own image and adding your own MQSC file into the `/etc/mqm` directory on the image. This file will be run when your queue manager is created.
|
||||
3. By using [remote MQ administration](https://www.ibm.com/support/knowledgecenter/SSFKSJ_9.1.0/com.ibm.mq.adm.doc/q021090_.htm), via an MQ command server, the MQ HTTP APIs, or using a tool such as the MQ web console or MQ Explorer.
|
||||
3. By using [remote MQ administration](https://www.ibm.com/support/knowledgecenter/SSFKSJ_9.3.0/com.ibm.mq.adm.doc/q021090_.htm), via an MQ command server, the MQ HTTP APIs, or using a tool such as the MQ web console or MQ Explorer.
|
||||
|
||||
Note that a listener is always created on port 1414 inside the container. This port can be mapped to any port on the Docker host.
|
||||
|
||||
The following is an *example* `Dockerfile` for creating your own pre-configured image, which adds a custom MQ configuration file, and an administrative user `alice`. Note that it is not normally recommended to include passwords in this way:
|
||||
The following is an *example* `Dockerfile` for creating your own pre-configured image, which adds a custom MQ configuration file:
|
||||
|
||||
```dockerfile
|
||||
FROM ibmcom/mq
|
||||
USER root
|
||||
RUN useradd alice -G mqm && \
|
||||
echo alice:passw0rd | chpasswd
|
||||
USER mqm
|
||||
FROM icr.io/ibm-messaging/mq
|
||||
USER 1001
|
||||
COPY 20-config.mqsc /etc/mqm/
|
||||
```
|
||||
|
||||
The `USER` instructions are necessary to ensure that the `useradd` and `chpasswd` commands are run as the root user.
|
||||
|
||||
Here is an example corresponding `20-config.mqsc` script, which creates two local queues:
|
||||
|
||||
```mqsc
|
||||
@@ -96,3 +93,22 @@ docker exec \
|
||||
```
|
||||
|
||||
Using this technique, you can have full control over all aspects of the MQ installation. Note that if you use this technique to make changes to the filesystem, then those changes would be lost if you re-created your container unless you make those changes in volumes.
|
||||
|
||||
## Supplying TLS certificates
|
||||
|
||||
If you wish to supply TLS Certificates that the queue manager and MQ Console should use for TLS operations then you must supply a PKCS#1 or unencrypted PKCS#8 PEM files for both the certificates and private keys in the following directories:
|
||||
|
||||
* `/etc/mqm/pki/keys/<Label>` - for certificates with public and private keys
|
||||
* `/etc/mqm/pki/trust/<index>` - for certificates with only the public key
|
||||
|
||||
For example, if you have an identity certificate you wish to add with the label `mykey` and 2 certificates you wish to add as trusted then you would need to add the files into the following locations where files ending in `.key` contain private keys and `.crt` contain certificates:
|
||||
|
||||
- `/etc/mqm/pki/keys/mykey/tls.key`
|
||||
- `/etc/mqm/pki/keys/mykey/tls.crt`
|
||||
- `/etc/mqm/pki/keys/mykey/ca.crt`
|
||||
- `/etc/mqm/pki/trust/0/tls.crt`
|
||||
- `/etc/mqm/pki/trust/1/tls.crt`
|
||||
|
||||
This can be achieved by either mounting the directories or files into the container when you run it or by baking the files into the correct location in the image.
|
||||
|
||||
If you supply multiple identity certificates then the first label alphabetically will be chosen as the certificate to be used by the MQ Console and the default certificate for the queue manager. If you wish to use a different certificate on the queue manager then you can change the certificate to use at runtime by executing the MQSC command `ALTER QMGR CERTLABL('<newlabel>')`
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
* © Copyright IBM Corporation 2017
|
||||
* © Copyright IBM Corporation 2019
|
||||
*
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -12,4 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
|
||||
REFRESH SECURITY TYPE(CONNAUTH)
|
||||
* Set the keystore location for the queue manager
|
||||
ALTER QMGR SSLKEYR('{{ .SSLKeyR }}')
|
||||
ALTER QMGR CERTLABL('{{ .CertificateLabel }}')
|
||||
REFRESH SECURITY(*) TYPE(SSL)
|
||||
2
etc/mqm/mq.htpasswd
Normal file
2
etc/mqm/mq.htpasswd
Normal file
@@ -0,0 +1,2 @@
|
||||
admin:$2y$05$M/C1U62RZ6q1kv4E7.S7ueNESJmFe85RsZcoMUReRXUDB8QcP3yqS
|
||||
app:$2y$05$BnbPtcjXTjk5JRJ8gzHqIuHgoQbLF3qtbPV3Q3tLyr0XJNg.7dkxW
|
||||
11
etc/mqm/qm-service-component.ini
Normal file
11
etc/mqm/qm-service-component.ini
Normal file
@@ -0,0 +1,11 @@
|
||||
ServiceComponent:
|
||||
Service=AuthorizationService
|
||||
Name=Dev.HtpAuth.Service
|
||||
Module=/opt/mqm/lib64/mqhtpass.so
|
||||
ComponentDataSize=0
|
||||
ServiceComponent:
|
||||
Service=AuthorizationService
|
||||
Name=MQSeries.UNIX.auth.service
|
||||
Module=amqzfu
|
||||
ComponentDataSize=0
|
||||
|
||||
53
glide.lock
generated
53
glide.lock
generated
@@ -1,53 +0,0 @@
|
||||
hash: b02555ebf3957ece0ae5ecf132fa4e415a4f66a7f4c27a82d484f4fb78f56e41
|
||||
updated: 2018-07-13T08:50:32.923040349+01:00
|
||||
imports:
|
||||
- name: github.com/beorn7/perks
|
||||
version: 3a771d992973f24aa725d07868b467d1ddfceafb
|
||||
subpackages:
|
||||
- quantile
|
||||
- name: github.com/genuinetools/amicontained
|
||||
version: fcae88544f0212fbb1e20699c41566655b68679b
|
||||
subpackages:
|
||||
- container
|
||||
- name: github.com/golang/protobuf
|
||||
version: 70b3af33377e7aa25ae42977bed93cc6b90f0373
|
||||
subpackages:
|
||||
- proto
|
||||
- name: github.com/ibm-messaging/mq-golang
|
||||
version: 1b2a2ad95ba3c555944be28097d392c27bda4071
|
||||
subpackages:
|
||||
- ibmmq
|
||||
- mqmetric
|
||||
- name: github.com/matttproud/golang_protobuf_extensions
|
||||
version: c12348ce28de40eed0136aa2b644d0ee0650e56c
|
||||
subpackages:
|
||||
- pbutil
|
||||
- name: github.com/prometheus/client_golang
|
||||
version: c5b7fccd204277076155f10851dad72b76a49317
|
||||
subpackages:
|
||||
- prometheus
|
||||
- name: github.com/prometheus/client_model
|
||||
version: 5c3871d89910bfb32f5fcab2aa4b9ec68e65a99f
|
||||
subpackages:
|
||||
- go
|
||||
- name: github.com/prometheus/common
|
||||
version: 7600349dcfe1abd18d72d3a1770870d9800a7801
|
||||
subpackages:
|
||||
- expfmt
|
||||
- internal/bitbucket.org/ww/goautoneg
|
||||
- model
|
||||
- name: github.com/prometheus/procfs
|
||||
version: ae68e2d4c00fed4943b5f6698d504a5fe083da8a
|
||||
subpackages:
|
||||
- internal/util
|
||||
- nfs
|
||||
- xfs
|
||||
- name: github.com/syndtr/gocapability
|
||||
version: 33e07d32887e1e06b7c025f27ce52f62c7990bc0
|
||||
subpackages:
|
||||
- capability
|
||||
- name: golang.org/x/sys
|
||||
version: 1b2967e3c290b7c545b3db0deeda16e9be4f98a2
|
||||
subpackages:
|
||||
- unix
|
||||
testImports: []
|
||||
28
glide.yaml
28
glide.yaml
@@ -1,28 +0,0 @@
|
||||
# © Copyright IBM Corporation 2017
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
package: github.com/ibm-messaging/mq-container
|
||||
license: Apache-2.0
|
||||
excludeDirs:
|
||||
- build
|
||||
- coverage
|
||||
- test
|
||||
import:
|
||||
- package: golang.org/x/sys/unix
|
||||
- package: github.com/prometheus/client_golang
|
||||
version: 0.8.0
|
||||
- package: github.com/ibm-messaging/mq-golang
|
||||
version: 2.0.0
|
||||
- package: github.com/genuinetools/amicontained
|
||||
version: 0.4.0
|
||||
24
go.mod
Normal file
24
go.mod
Normal file
@@ -0,0 +1,24 @@
|
||||
module github.com/ibm-messaging/mq-container
|
||||
|
||||
go 1.19
|
||||
|
||||
require (
|
||||
github.com/genuinetools/amicontained v0.4.3
|
||||
github.com/ibm-messaging/mq-golang v2.0.0+incompatible
|
||||
github.com/prometheus/client_golang v1.11.1
|
||||
github.com/prometheus/client_model v0.2.0
|
||||
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1
|
||||
software.sslmate.com/src/go-pkcs12 v0.0.0-20200830195227-52f69702a001
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.1.1 // indirect
|
||||
github.com/golang/protobuf v1.4.3 // indirect
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
|
||||
github.com/prometheus/common v0.26.0 // indirect
|
||||
github.com/prometheus/procfs v0.6.0 // indirect
|
||||
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635 // indirect
|
||||
google.golang.org/protobuf v1.26.0-rc.1 // indirect
|
||||
)
|
||||
149
go.sum
Normal file
149
go.sum
Normal file
@@ -0,0 +1,149 @@
|
||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=
|
||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/genuinetools/amicontained v0.4.3 h1:cqq9XiAHfWWY3dk8VU8bSJFu9yh8Il5coEdeTAPq72o=
|
||||
github.com/genuinetools/amicontained v0.4.3/go.mod h1:PAMZkg9CcUTa6gNyULQ6tOMTMEb2HTKJufvKeFqDw+o=
|
||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
|
||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM=
|
||||
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/ibm-messaging/mq-golang v2.0.0+incompatible h1:xAufRPYSzoRGaME2+x7LcW5+uvy/G3xL/3Sn3u+G/lY=
|
||||
github.com/ibm-messaging/mq-golang v2.0.0+incompatible/go.mod h1:qjsZDb7m1oKnbPeDma2JVJTKgyCA91I4bcJ1qHY+gcA=
|
||||
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
|
||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
|
||||
github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
|
||||
github.com/prometheus/client_golang v1.11.1 h1:+4eQaD7vAZ6DsfsxB15hbE0odUjGI5ARs9yskGu1v4s=
|
||||
github.com/prometheus/client_golang v1.11.1/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=
|
||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M=
|
||||
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
|
||||
github.com/prometheus/common v0.26.0 h1:iMAkS2TDoNWnKM+Kopnx/8tnEStIfpYA0ur0xQzzhMQ=
|
||||
github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc=
|
||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
|
||||
github.com/prometheus/procfs v0.6.0 h1:mxy4L2jP6qMonqmq+aTtOx1ifVWUgG/TAmntgbh3xv4=
|
||||
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
|
||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635 h1:kdXcSzyDtseVEc4yCz2qF8ZrQvIDBJLl4S1c3GCXmoI=
|
||||
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d h1:sK3txAijHtOK88l68nt020reeT1ZdKLIYetKl95FzVY=
|
||||
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 h1:SrN+KX8Art/Sf4HNj6Zcz06G7VEz+7w9tdXTPOZ7+l4=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.26.0-rc.1 h1:7QnIQpGRHE5RnLKnESfDoxm2dTapTZua5a0kS0A+VXQ=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
software.sslmate.com/src/go-pkcs12 v0.0.0-20200830195227-52f69702a001 h1:AVd6O+azYjVQYW1l55IqkbL8/JxjrLtO6q4FCmV8N5c=
|
||||
software.sslmate.com/src/go-pkcs12 v0.0.0-20200830195227-52f69702a001/go.mod h1:/xvNRWUqm0+/ZMiF4EX00vrSCMsE4/NHb+Pt3freEeQ=
|
||||
18
ha/native-ha.ini.tpl
Normal file
18
ha/native-ha.ini.tpl
Normal file
@@ -0,0 +1,18 @@
|
||||
NativeHALocalInstance:
|
||||
Name={{ .Name }}
|
||||
{{ if .CertificateLabel }}
|
||||
CertificateLabel={{ .CertificateLabel }}
|
||||
KeyRepository={{ .KeyRepository }}
|
||||
{{ if .CipherSpec }}
|
||||
CipherSpec={{ .CipherSpec }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
NativeHAInstance:
|
||||
Name={{ .NativeHAInstance0_Name }}
|
||||
ReplicationAddress={{ .NativeHAInstance0_ReplicationAddress }}
|
||||
NativeHAInstance:
|
||||
Name={{ .NativeHAInstance1_Name }}
|
||||
ReplicationAddress={{ .NativeHAInstance1_ReplicationAddress }}
|
||||
NativeHAInstance:
|
||||
Name={{ .NativeHAInstance2_Name }}
|
||||
ReplicationAddress={{ .NativeHAInstance2_ReplicationAddress }}
|
||||
@@ -12,29 +12,33 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
FROM ubuntu:16.04
|
||||
FROM registry.redhat.io/ubi8/ubi-minimal AS mq-explorer
|
||||
|
||||
# The URL to download the MQ installer from in tar.gz format
|
||||
ARG MQ_URL=https://public.dhe.ibm.com/ibmdl/export/pub/software/websphere/messaging/mqadv/mqadv_dev911_ubuntu_x86-64.tar.gz
|
||||
ARG MQ_URL="https://public.dhe.ibm.com/ibmdl/export/pub/software/websphere/messaging/mqadv/mqadv_dev912_linux_x86-64.tar.gz"
|
||||
|
||||
# The MQ packages to install
|
||||
ARG MQ_PACKAGES
|
||||
ENV MQ_PACKAGES="MQSeriesRuntime*.rpm MQSeriesJRE*.rpm MQSeriesExplorer*.rpm"
|
||||
|
||||
ARG MQM_UID=999
|
||||
ARG MQM_UID=888
|
||||
|
||||
RUN export DEBIAN_FRONTEND=noninteractive \
|
||||
&& apt-get update \
|
||||
&& apt-get install -y \
|
||||
libgtk2.0-0 \
|
||||
libxtst6
|
||||
RUN microdnf install -y gtk2 libXtst \
|
||||
&& microdnf clean all
|
||||
|
||||
ADD install-mq.sh /usr/local/bin/
|
||||
|
||||
# Install MQ Explorer. To avoid a "text file busy" error here, we sleep before installing.
|
||||
# Need to re-instate the `/var/mqm` directory after installation, to avoid MQ
|
||||
# errors with some commands (e.g. `dspmqver`)
|
||||
RUN chmod u+x /usr/local/bin/install-mq.sh \
|
||||
&& install-mq.sh
|
||||
&& sleep 1 \
|
||||
&& install-mq.sh $MQM_UID \
|
||||
&& rm -rf /var/mqm \
|
||||
&& /opt/mqm/bin/crtmqdir -f -s
|
||||
|
||||
ENV LANG=en_US.UTF-8
|
||||
|
||||
# Run as mqm (999)
|
||||
USER 999
|
||||
# Run as mqm
|
||||
USER $MQM_UID
|
||||
|
||||
ENTRYPOINT ["MQExplorer"]
|
||||
@@ -1,33 +0,0 @@
|
||||
# © Copyright IBM Corporation 2018, 2019
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
ARG BASE_IMAGE=mq-sdk:9.1.1.0-x86_64-ubuntu-16.04
|
||||
|
||||
FROM $BASE_IMAGE
|
||||
|
||||
COPY incubating/mq-golang-sdk/install-golang.sh /usr/local/bin
|
||||
|
||||
ENV GO_VERSION=1.10
|
||||
|
||||
ENV PATH="${PATH}:/usr/lib/go-${GO_VERSION}/bin:/go/bin:/usr/local/go/bin" \
|
||||
CGO_CFLAGS="-I/opt/mqm/inc/" \
|
||||
CGO_LDFLAGS_ALLOW="-Wl,-rpath.*" \
|
||||
GOPATH="/go"
|
||||
|
||||
# Install the Go compiler and Git
|
||||
RUN chmod +x /usr/local/bin/install-golang.sh \
|
||||
&& sleep 1 \
|
||||
&& install-golang.sh
|
||||
|
||||
WORKDIR $GOPATH
|
||||
@@ -1,5 +0,0 @@
|
||||
# IBM MQ Software Developer Kit (SDK) with Go
|
||||
|
||||
This image contains the MQ SDK, Git, the Go compiler, and the `build-essential` package (which includes GNU C and C++ compilers plus other essential tools like `make`).
|
||||
|
||||
This image doesn't contain any Go code for MQ. You can add a CGO wrapper for the MQ C client, for example [mq-golang](https://github.com/ibm-messaging/mq-golang), via your vendor directory, or directly using `go get`.
|
||||
@@ -12,19 +12,19 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
ARG BASE_IMAGE=ubuntu:16.04
|
||||
|
||||
FROM $BASE_IMAGE
|
||||
FROM registry.redhat.io/rhel8/llvm-toolset:8.0.1-10 AS mq-sdk
|
||||
#FROM docker.io/centos/devtoolset-7-toolchain-centos7 AS mq-sdk
|
||||
|
||||
# The URL to download the MQ installer from in tar.gz format
|
||||
# This assumes an archive containing the MQ Debian (.deb) install packages
|
||||
ARG MQ_URL
|
||||
|
||||
# The packages to install in install-mq.sh
|
||||
ARG MQ_PACKAGES
|
||||
ENV MQ_PACKAGES="MQSeriesRuntime-*.rpm MQSeriesSDK-*.rpm MQSeriesSamples*.rpm"
|
||||
|
||||
ARG MQM_UID=999
|
||||
ENV MQM_UID=888
|
||||
|
||||
USER 0
|
||||
COPY install-mq.sh /usr/local/bin/
|
||||
|
||||
# Install MQ. To avoid a "text file busy" error here, we sleep before installing.
|
||||
@@ -35,3 +35,4 @@ RUN chmod u+x /usr/local/bin/install-mq.sh \
|
||||
&& install-mq.sh $MQM_UID \
|
||||
&& rm -rf /var/mqm \
|
||||
&& /opt/mqm/bin/crtmqdir -f -s
|
||||
USER 1001
|
||||
@@ -13,8 +13,6 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
|
||||
STOP LISTENER('SYSTEM.LISTENER.TCP.1') IGNSTATE(YES)
|
||||
|
||||
* Developer queues
|
||||
DEFINE QLOCAL('DEV.QUEUE.1') REPLACE
|
||||
DEFINE QLOCAL('DEV.QUEUE.2') REPLACE
|
||||
@@ -42,12 +40,9 @@ SET CHLAUTH('*') TYPE(ADDRESSMAP) ADDRESS('*') USERSRC(NOACCESS) DESCR('Back-sto
|
||||
SET CHLAUTH('DEV.APP.SVRCONN') TYPE(ADDRESSMAP) ADDRESS('*') USERSRC(CHANNEL) CHCKCLNT({{ .ChckClnt }}) DESCR('Allows connection via APP channel') ACTION(REPLACE)
|
||||
SET CHLAUTH('DEV.ADMIN.SVRCONN') TYPE(BLOCKUSER) USERLIST('nobody') DESCR('Allows admins on ADMIN channel') ACTION(REPLACE)
|
||||
SET CHLAUTH('DEV.ADMIN.SVRCONN') TYPE(USERMAP) CLNTUSER('admin') USERSRC(CHANNEL) DESCR('Allows admin user to connect via ADMIN channel') ACTION(REPLACE)
|
||||
SET CHLAUTH('DEV.ADMIN.SVRCONN') TYPE(USERMAP) CLNTUSER('admin') USERSRC(MAP) MCAUSER ('mqm') DESCR ('Allow admin as MQ-admin') ACTION(REPLACE)
|
||||
|
||||
* Developer authority records
|
||||
SET AUTHREC GROUP('mqclient') OBJTYPE(QMGR) AUTHADD(CONNECT,INQ)
|
||||
SET AUTHREC PROFILE('DEV.**') GROUP('mqclient') OBJTYPE(QUEUE) AUTHADD(BROWSE,GET,INQ,PUT)
|
||||
SET AUTHREC PROFILE('DEV.**') GROUP('mqclient') OBJTYPE(TOPIC) AUTHADD(PUB,SUB)
|
||||
|
||||
* Developer listener
|
||||
DEFINE LISTENER('DEV.LISTENER.TCP') TRPTYPE(TCP) PORT(1414) CONTROL(QMGR) REPLACE
|
||||
START LISTENER('DEV.LISTENER.TCP') IGNSTATE(YES)
|
||||
SET AUTHREC PRINCIPAL('app') OBJTYPE(QMGR) AUTHADD(CONNECT,INQ)
|
||||
SET AUTHREC PROFILE('DEV.**') PRINCIPAL('app') OBJTYPE(QUEUE) AUTHADD(BROWSE,GET,INQ,PUT)
|
||||
SET AUTHREC PROFILE('DEV.**') PRINCIPAL('app') OBJTYPE(TOPIC) AUTHADD(PUB,SUB)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
* © Copyright IBM Corporation 2018
|
||||
* © Copyright IBM Corporation 2018, 2022
|
||||
*
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@@ -13,10 +13,6 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
|
||||
* Set the keystore location for the queue manager
|
||||
ALTER QMGR SSLKEYR('{{ .SSLKeyR }}')
|
||||
ALTER QMGR CERTLABL('{{ .CertificateLabel }}')
|
||||
|
||||
* Set the cipherspec for dev channels
|
||||
ALTER CHANNEL('DEV.APP.SVRCONN') CHLTYPE(SVRCONN) SSLCIPH({{ .SSLCipherSpec }}) SSLCAUTH(OPTIONAL)
|
||||
ALTER CHANNEL('DEV.ADMIN.SVRCONN') CHLTYPE(SVRCONN) SSLCIPH({{ .SSLCipherSpec }}) SSLCAUTH(OPTIONAL)
|
||||
ALTER CHANNEL('DEV.APP.SVRCONN') CHLTYPE(SVRCONN) SSLCIPH(ANY_TLS12_OR_HIGHER) SSLCAUTH(OPTIONAL)
|
||||
ALTER CHANNEL('DEV.ADMIN.SVRCONN') CHLTYPE(SVRCONN) SSLCIPH(ANY_TLS12_OR_HIGHER) SSLCAUTH(OPTIONAL)
|
||||
|
||||
@@ -1,86 +0,0 @@
|
||||
# © Copyright IBM Corporation 2015, 2019
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
ARG BASE_IMAGE=mqadvanced-server-dev-base:9.1.2.0-x86_64-ubuntu-16.04
|
||||
ARG BUILDER_IMAGE=mq-golang-sdk:9.1.2.0-x86_64-ubuntu-16.04
|
||||
|
||||
###############################################################################
|
||||
# Build stage to build Go code
|
||||
###############################################################################
|
||||
FROM $BUILDER_IMAGE as builder
|
||||
ARG IMAGE_REVISION="Not specified"
|
||||
ARG IMAGE_SOURCE="Not specified"
|
||||
ARG IMAGE_TAG="Not specified"
|
||||
WORKDIR /go/src/github.com/ibm-messaging/mq-container/
|
||||
COPY cmd/ ./cmd
|
||||
COPY internal/ ./internal
|
||||
COPY vendor/ ./vendor
|
||||
# Re-build runmqserver, with code tagged with 'mqdev' enabled
|
||||
RUN go build -ldflags "-X \"main.ImageCreated=$(date --iso-8601=seconds)\" -X \"main.ImageRevision=$IMAGE_REVISION\" -X \"main.ImageSource=$IMAGE_SOURCE\" -X \"main.ImageTag=$IMAGE_TAG\"" --tags 'mqdev' ./cmd/runmqserver
|
||||
RUN go build ./cmd/runmqdevserver/
|
||||
# Run all unit tests
|
||||
RUN go test -v ./cmd/runmqdevserver/...
|
||||
|
||||
###############################################################################
|
||||
# Main build stage
|
||||
###############################################################################
|
||||
FROM $BASE_IMAGE
|
||||
|
||||
# Enable MQ developer default configuration
|
||||
ENV MQ_DEV=true
|
||||
|
||||
# Default administrator password
|
||||
ENV MQ_ADMIN_PASSWORD=passw0rd
|
||||
|
||||
ARG MQM_UID=999
|
||||
|
||||
USER root
|
||||
|
||||
COPY incubating/mqadvanced-server-dev/install-extra-packages.sh /usr/local/bin/
|
||||
|
||||
RUN chmod u+x /usr/local/bin/install-extra-packages.sh \
|
||||
&& sleep 1 \
|
||||
&& install-extra-packages.sh
|
||||
|
||||
# WARNING: This is what allows the mqm user to change the password of any other user
|
||||
# It's used by runmqdevserver to change the admin/app passwords.
|
||||
RUN echo "mqm ALL = NOPASSWD: /usr/sbin/chpasswd" > /etc/sudoers.d/mq-dev-config
|
||||
|
||||
## Add admin and app users, and set a default password for admin
|
||||
RUN useradd admin -G mqm \
|
||||
&& groupadd mqclient \
|
||||
&& useradd app -G mqclient \
|
||||
&& echo admin:$MQ_ADMIN_PASSWORD | chpasswd
|
||||
|
||||
# Create a directory for runtime data from runmqserver
|
||||
RUN mkdir -p /run/runmqdevserver \
|
||||
&& chown mqm:mqm /run/runmqdevserver
|
||||
|
||||
COPY --from=builder /go/src/github.com/ibm-messaging/mq-container/runmqserver /usr/local/bin/
|
||||
COPY --from=builder /go/src/github.com/ibm-messaging/mq-container/runmqdevserver /usr/local/bin/
|
||||
|
||||
# Copy template files
|
||||
COPY incubating/mqadvanced-server-dev/*.tpl /etc/mqm/
|
||||
# Copy web XML files for default developer configuration
|
||||
COPY incubating/mqadvanced-server-dev/web /etc/mqm/web
|
||||
|
||||
RUN chown -R mqm:mqm /etc/mqm/* \
|
||||
&& chmod +x /usr/local/bin/runmq* \
|
||||
&& install --directory --mode 0775 --owner mqm --group root /run/runmqdevserver
|
||||
|
||||
EXPOSE 9443
|
||||
|
||||
USER $MQM_UID
|
||||
|
||||
ENTRYPOINT ["runmqdevserver"]
|
||||
@@ -1,17 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Change admin password
|
||||
if [ -n "${MQ_ADMIN_PASSWORD}" ]; then
|
||||
echo admin:${MQ_ADMIN_PASSWORD} | chpasswd
|
||||
fi
|
||||
# Change app password
|
||||
if [ -n "${MQ_APP_PASSWORD}" ]; then
|
||||
echo app:${MQ_APP_PASSWORD} | chpasswd
|
||||
fi
|
||||
|
||||
# Delete the MQSC with developer defaults, if requested
|
||||
if [ "${MQ_DEV}" != "true" ]; then
|
||||
rm -f /etc/mqm/dev.mqsc
|
||||
fi
|
||||
|
||||
exec runmqserver
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/bin/bash
|
||||
# -*- mode: sh -*-
|
||||
# © Copyright IBM Corporation 2019
|
||||
# © Copyright IBM Corporation 2019, 2021
|
||||
#
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@@ -15,18 +15,27 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
test -f /usr/bin/yum && RHEL=true || RHEL=false
|
||||
# Fail on any non-zero return code
|
||||
set -ex
|
||||
|
||||
test -f /usr/bin/yum && YUM=true || YUM=false
|
||||
test -f /usr/bin/microdnf && MICRODNF=true || MICRODNF=false
|
||||
test -f /usr/bin/apt-get && UBUNTU=true || UBUNTU=false
|
||||
|
||||
if ($UBUNTU); then
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
apt-get update
|
||||
apt-get install -y --no-install-recommends sudo
|
||||
apt-get install -y --no-install-recommends libaprutil1
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
fi
|
||||
|
||||
if ($RHEL); then
|
||||
yum -y install sudo
|
||||
if ($YUM); then
|
||||
yum -y install apr-util-openssl
|
||||
yum -y clean all
|
||||
rm -rf /var/cache/yum/*
|
||||
fi
|
||||
|
||||
if ($MICRODNF); then
|
||||
microdnf --disableplugin=subscription-manager install apr-util-openssl
|
||||
microdnf --disableplugin=subscription-manager clean all
|
||||
fi
|
||||
@@ -36,5 +36,9 @@
|
||||
</basicRegistry>
|
||||
<variable name="httpHost" value="*"/>
|
||||
<variable name="managementMode" value="externallyprovisioned"/>
|
||||
<variable name="mqConsoleRemoteSupportEnabled" value="false"/>
|
||||
<variable name="mqConsoleEnableUnsafeInline" value="true"/>
|
||||
<jndiEntry jndiName="mqConsoleDefaultCCDTHostname" value="${env.MQ_CONSOLE_DEFAULT_CCDT_HOSTNAME}"/>
|
||||
<jndiEntry jndiName="mqConsoleDefaultCCDTPort" value="${env.MQ_CONSOLE_DEFAULT_CCDT_PORT}"/>
|
||||
<include location="tls.xml"/>
|
||||
</server>
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<server>
|
||||
<keyStore id="MQWebKeyStore" location="/run/runmqdevserver/tls/key.jks" type="JKS" password="${env.MQ_TLS_PASSPHRASE}"/>
|
||||
<keyStore id="MQWebTrustStore" location="/run/runmqdevserver/tls/trust.jks" type="JKS" password="${env.MQ_TLS_PASSPHRASE}"/>
|
||||
<ssl id="thisSSLConfig" clientAuthenticationSupported="true" keyStoreRef="MQWebKeyStore" trustStoreRef="MQWebTrustStore" sslProtocol="TLSv1.2" serverKeyAlias="devcert"/>
|
||||
<sslDefault sslRef="thisSSLConfig"/>
|
||||
</server>
|
||||
@@ -19,8 +19,10 @@
|
||||
|
||||
set -ex
|
||||
|
||||
curl https://glide.sh/get | sh
|
||||
sudo curl -Lo /usr/local/bin/dep https://github.com/golang/dep/releases/download/v0.5.0/dep-linux-amd64
|
||||
sudo curl -Lo /usr/local/bin/dep https://github.com/golang/dep/releases/download/v0.5.1/dep-linux-$ARCH
|
||||
sudo chmod +x /usr/local/bin/dep
|
||||
sudo apt-get update || :
|
||||
sudo apt-get install -y jq
|
||||
|
||||
go get -u golang.org/x/lint/golint
|
||||
go install golang.org/x/lint/golint@latest
|
||||
curl -sfL https://raw.githubusercontent.com/securego/gosec/master/install.sh | sh -s -- -b $GOPATH/bin v2.14.0 || echo "Gosec not installed. Platform may not be supported."
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/bin/bash
|
||||
# -*- mode: sh -*-
|
||||
# © Copyright IBM Corporation 2018
|
||||
# © Copyright IBM Corporation 2015, 2021
|
||||
#
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@@ -18,7 +18,9 @@
|
||||
# Fail on any non-zero return code
|
||||
set -ex
|
||||
|
||||
test -f /usr/bin/yum && RHEL=true || RHEL=false
|
||||
test -f /usr/bin/yum && YUM=true || YUM=false
|
||||
test -f /usr/bin/microdnf && MICRODNF=true || MICRODNF=false
|
||||
test -f /usr/bin/rpm && RPM=true || RPM=false
|
||||
test -f /usr/bin/apt-get && UBUNTU=true || UBUNTU=false
|
||||
|
||||
if ($UBUNTU); then
|
||||
@@ -37,37 +39,43 @@ if ($UBUNTU); then
|
||||
# This ensures no unsupported code gets installed, and makes the build faster
|
||||
echo "deb ${APT_URL} ${UBUNTU_CODENAME} main restricted" > /etc/apt/sources.list
|
||||
echo "deb ${APT_URL} ${UBUNTU_CODENAME}-updates main restricted" >> /etc/apt/sources.list
|
||||
echo "deb ${APT_URL} ${UBUNTU_CODENAME}-backports main restricted universe" >> /etc/apt/sources.list;
|
||||
echo "deb ${APT_URL} ${UBUNTU_CODENAME}-security main restricted" >> /etc/apt/sources.list
|
||||
|
||||
apt-get update
|
||||
apt-get install -y --no-install-recommends \
|
||||
golang-${GO_VERSION} \
|
||||
git \
|
||||
ca-certificates
|
||||
fi
|
||||
|
||||
if ($RHEL); then
|
||||
# Install additional packages required by MQ, this install process and the runtime scripts
|
||||
yum -y install \
|
||||
git \
|
||||
apt-get update
|
||||
apt-get install -y --no-install-recommends \
|
||||
bash \
|
||||
bc \
|
||||
ca-certificates \
|
||||
coreutils \
|
||||
curl \
|
||||
debianutils \
|
||||
file \
|
||||
findutils \
|
||||
gawk \
|
||||
grep \
|
||||
libc-bin \
|
||||
mount \
|
||||
passwd \
|
||||
procps \
|
||||
sed \
|
||||
tar \
|
||||
gcc
|
||||
|
||||
cd /tmp
|
||||
curl -LO https://storage.googleapis.com/golang/go${GO_VERSION}.linux-amd64.tar.gz
|
||||
tar -C /usr/local -xzf go${GO_VERSION}.linux-amd64.tar.gz
|
||||
util-linux
|
||||
fi
|
||||
|
||||
# Remove any orphaned packages
|
||||
$UBUNTU && apt-get autoremove -y
|
||||
if ($RPM); then
|
||||
EXTRA_RPMS="bash bc ca-certificates file findutils gawk glibc-common grep ncurses-compat-libs passwd procps-ng sed shadow-utils tar util-linux which"
|
||||
# Install additional packages required by MQ, this install process and the runtime scripts
|
||||
$YUM && yum -y install --setopt install_weak_deps=false ${EXTRA_RPMS}
|
||||
$MICRODNF && microdnf --disableplugin=subscription-manager install ${EXTRA_RPMS}
|
||||
fi
|
||||
|
||||
# Apply any bug fixes not included in base Ubuntu or MQ image.
|
||||
# Don't upgrade everything based on Docker best practices https://docs.docker.com/engine/userguide/eng-image/dockerfile_best-practices/#run
|
||||
$UBUNTU && apt-get install -y libapparmor1 libsystemd0 systemd systemd-sysv libudev1 perl-base --only-upgrade
|
||||
# End of bug fixes
|
||||
|
||||
# Clean up cached files
|
||||
$UBUNTU && rm -rf /var/lib/apt/lists/*
|
||||
$RHEL && yum -y clean all
|
||||
$RHEL && rm -rf /var/cache/yum/*
|
||||
|
||||
# Make the GOLANG directories
|
||||
mkdir -p $GOPATH/src $GOPATH/bin
|
||||
chmod -R 777 $GOPATH
|
||||
$YUM && yum -y clean all
|
||||
$YUM && rm -rf /var/cache/yum/*
|
||||
$MICRODNF && microdnf --disableplugin=subscription-manager clean all
|
||||
192
install-mq.sh
192
install-mq.sh
@@ -1,6 +1,6 @@
|
||||
#!/bin/bash
|
||||
# -*- mode: sh -*-
|
||||
# © Copyright IBM Corporation 2015, 2019
|
||||
# © Copyright IBM Corporation 2015, 2022
|
||||
#
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@@ -18,147 +18,64 @@
|
||||
# Fail on any non-zero return code
|
||||
set -ex
|
||||
|
||||
mqm_uid=${1:-999}
|
||||
|
||||
test -f /usr/bin/yum && RHEL=true || RHEL=false
|
||||
test -f /usr/bin/rpm && RPM=true || RPM=false
|
||||
test -f /usr/bin/apt-get && UBUNTU=true || UBUNTU=false
|
||||
|
||||
# If MQ_PACKAGES isn't specifically set, then choose a valid set of defaults
|
||||
if [ -z "$MQ_PACKAGES" ]; then
|
||||
$UBUNTU && MQ_PACKAGES="ibmmq-server ibmmq-java ibmmq-jre ibmmq-gskit ibmmq-msg-.* ibmmq-samples ibmmq-ams"
|
||||
$RHEL && MQ_PACKAGES="MQSeriesRuntime-*.rpm MQSeriesServer-*.rpm MQSeriesJava*.rpm MQSeriesJRE*.rpm MQSeriesGSKit*.rpm MQSeriesMsg*.rpm MQSeriesSamples*.rpm MQSeriesAMS-*.rpm"
|
||||
fi
|
||||
# Download and extract the MQ unzippable server
|
||||
DIR_TMP=/tmp/mq
|
||||
mkdir -p ${DIR_TMP}
|
||||
cd ${DIR_TMP}
|
||||
curl --fail --location $MQ_URL | tar --extract --gunzip
|
||||
ls -la ${DIR_TMP}
|
||||
|
||||
if ($UBUNTU); then
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
# Use a reduced set of apt repositories.
|
||||
# This ensures no unsupported code gets installed, and makes the build faster
|
||||
source /etc/os-release
|
||||
# Figure out the correct apt URL based on the CPU architecture
|
||||
CPU_ARCH=$(uname -p)
|
||||
if [ ${CPU_ARCH} == "x86_64" ]; then
|
||||
APT_URL="http://archive.ubuntu.com/ubuntu/"
|
||||
else
|
||||
APT_URL="http://ports.ubuntu.com/ubuntu-ports/"
|
||||
fi
|
||||
# Use a reduced set of apt repositories.
|
||||
# This ensures no unsupported code gets installed, and makes the build faster
|
||||
echo "deb ${APT_URL} ${UBUNTU_CODENAME} main restricted" > /etc/apt/sources.list
|
||||
echo "deb ${APT_URL} ${UBUNTU_CODENAME}-updates main restricted" >> /etc/apt/sources.list
|
||||
echo "deb ${APT_URL} ${UBUNTU_CODENAME}-security main restricted" >> /etc/apt/sources.list
|
||||
# Install additional packages required by MQ, this install process and the runtime scripts
|
||||
apt-get update
|
||||
apt-get install -y --no-install-recommends \
|
||||
bash \
|
||||
bc \
|
||||
ca-certificates \
|
||||
coreutils \
|
||||
curl \
|
||||
debianutils \
|
||||
file \
|
||||
findutils \
|
||||
gawk \
|
||||
grep \
|
||||
libc-bin \
|
||||
mount \
|
||||
passwd \
|
||||
procps \
|
||||
sed \
|
||||
tar \
|
||||
util-linux
|
||||
fi
|
||||
|
||||
# Install additional packages required by MQ, this install process and the runtime scripts
|
||||
$RHEL && yum -y install \
|
||||
bash \
|
||||
bc \
|
||||
ca-certificates \
|
||||
coreutils \
|
||||
curl \
|
||||
file \
|
||||
findutils \
|
||||
gawk \
|
||||
glibc-common \
|
||||
grep \
|
||||
passwd \
|
||||
procps-ng \
|
||||
sed \
|
||||
tar \
|
||||
util-linux
|
||||
|
||||
# Download and extract the MQ installation files
|
||||
DIR_EXTRACT=/tmp/mq
|
||||
mkdir -p ${DIR_EXTRACT}
|
||||
cd ${DIR_EXTRACT}
|
||||
curl -LO $MQ_URL
|
||||
tar -zxvf ./*.tar.gz
|
||||
|
||||
# Remove packages only needed by this script
|
||||
$UBUNTU && apt-get purge -y \
|
||||
ca-certificates \
|
||||
curl
|
||||
|
||||
# Note: ca-certificates and curl are installed by default in RHEL
|
||||
|
||||
# Remove any orphaned packages
|
||||
$UBUNTU && apt-get autoremove -y
|
||||
|
||||
# Recommended: Create the mqm user ID with a fixed UID and group, so that the file permissions work between different images
|
||||
groupadd --system --gid ${mqm_uid} mqm
|
||||
useradd --system --uid ${mqm_uid} --gid mqm --groups 0 mqm
|
||||
|
||||
# Find directory containing .deb files
|
||||
$UBUNTU && DIR_DEB=$(find ${DIR_EXTRACT} -name "*.deb" -printf "%h\n" | sort -u | head -1)
|
||||
$RHEL && DIR_RPM=$(find ${DIR_EXTRACT} -name "*.rpm" -printf "%h\n" | sort -u | head -1)
|
||||
# Find location of mqlicense.sh
|
||||
MQLICENSE=$(find ${DIR_EXTRACT} -name "mqlicense.sh")
|
||||
# Generate MQ package in INSTALLATION_DIR
|
||||
export genmqpkg_inc32=0
|
||||
export genmqpkg_incadm=1
|
||||
export genmqpkg_incamqp=0
|
||||
export genmqpkg_incams=1
|
||||
export genmqpkg_inccbl=0
|
||||
export genmqpkg_inccics=0
|
||||
export genmqpkg_inccpp=0
|
||||
export genmqpkg_incdnet=0
|
||||
export genmqpkg_incjava=1
|
||||
export genmqpkg_incjre=1
|
||||
export genmqpkg_incman=0
|
||||
export genmqpkg_incmqbc=0
|
||||
export genmqpkg_incmqft=0
|
||||
export genmqpkg_incmqsf=0
|
||||
export genmqpkg_incmqxr=0
|
||||
export genmqpkg_incnls=1
|
||||
export genmqpkg_incras=1
|
||||
export genmqpkg_incsamp=1
|
||||
export genmqpkg_incsdk=0
|
||||
export genmqpkg_inctls=1
|
||||
export genmqpkg_incunthrd=0
|
||||
export genmqpkg_incweb=1
|
||||
export INSTALLATION_DIR=/opt/mqm
|
||||
${DIR_TMP}/bin/genmqpkg.sh -b ${INSTALLATION_DIR}
|
||||
ls -la ${INSTALLATION_DIR}
|
||||
rm -rf ${DIR_TMP}
|
||||
|
||||
# Accept the MQ license
|
||||
${MQLICENSE} -text_only -accept
|
||||
$UBUNTU && echo "deb [trusted=yes] file:${DIR_DEB} ./" > /etc/apt/sources.list.d/IBM_MQ.list
|
||||
|
||||
# Install MQ using the DEB packages
|
||||
$UBUNTU && apt-get update
|
||||
$UBUNTU && apt-get install -y $MQ_PACKAGES
|
||||
|
||||
$RHEL && cd $DIR_RPM && rpm -ivh $MQ_PACKAGES
|
||||
|
||||
# Remove 32-bit libraries from 64-bit container
|
||||
find /opt/mqm /var/mqm -type f -exec file {} \; | awk -F: '/ELF 32-bit/{print $1}' | xargs --no-run-if-empty rm -f
|
||||
|
||||
# Remove tar.gz files unpacked by RPM postinst scripts
|
||||
find /opt/mqm -name '*.tar.gz' -delete
|
||||
|
||||
# Recommended: Set the default MQ installation (makes the MQ commands available on the PATH)
|
||||
/opt/mqm/bin/setmqinst -p /opt/mqm -i
|
||||
|
||||
# Clean up all the downloaded files
|
||||
$UBUNTU && rm -f /etc/apt/sources.list.d/IBM_MQ.list
|
||||
rm -rf ${DIR_EXTRACT}
|
||||
|
||||
# Apply any bug fixes not included in base Ubuntu or MQ image.
|
||||
# Don't upgrade everything based on Docker best practices https://docs.docker.com/engine/userguide/eng-image/dockerfile_best-practices/#run
|
||||
$UBUNTU && apt-get install -y libapparmor1 libsystemd0 systemd systemd-sysv libudev1 perl-base --only-upgrade
|
||||
# End of bug fixes
|
||||
|
||||
# Clean up cached files
|
||||
$UBUNTU && rm -rf /var/lib/apt/lists/*
|
||||
$RHEL && yum -y clean all
|
||||
$RHEL && rm -rf /var/cache/yum/*
|
||||
${INSTALLATION_DIR}/bin/mqlicense -accept
|
||||
|
||||
# Optional: Update the command prompt with the MQ version
|
||||
$UBUNTU && echo "mq:$(dspmqver -b -f 2)" > /etc/debian_chroot
|
||||
|
||||
# Remove the directory structure under /var/mqm which was created by the installer
|
||||
rm -rf /var/mqm
|
||||
|
||||
# Create the mount point for volumes, ensuring MQ has permissions to all directories
|
||||
install --directory --mode 0775 --owner mqm --group root /mnt
|
||||
install --directory --mode 0775 --owner mqm --group root /mnt/mqm
|
||||
install --directory --mode 0775 --owner mqm --group root /mnt/mqm/data
|
||||
install --directory --mode 2775 --owner 1001 --group root /mnt
|
||||
install --directory --mode 2775 --owner 1001 --group root /mnt/mqm
|
||||
install --directory --mode 2775 --owner 1001 --group root /mnt/mqm/data
|
||||
install --directory --mode 2775 --owner 1001 --group root /mnt/mqm-log
|
||||
install --directory --mode 2775 --owner 1001 --group root /mnt/mqm-log/log
|
||||
install --directory --mode 2775 --owner 1001 --group root /mnt/mqm-data
|
||||
install --directory --mode 2775 --owner 1001 --group root /mnt/mqm-data/qmgrs
|
||||
|
||||
# Create the directory for MQ configuration files
|
||||
install --directory --mode 0775 --owner mqm --group root /etc/mqm
|
||||
install --directory --mode 2775 --owner 1001 --group root /etc/mqm
|
||||
|
||||
# Create the directory for MQ runtime files
|
||||
install --directory --mode 2775 --owner 1001 --group root /run/mqm
|
||||
|
||||
# Create a symlink for /var/mqm -> /mnt/mqm/data
|
||||
ln -s /mnt/mqm/data /var/mqm
|
||||
@@ -166,7 +83,20 @@ ln -s /mnt/mqm/data /var/mqm
|
||||
# Optional: Ensure any passwords expire in a timely manner
|
||||
sed -i 's/PASS_MAX_DAYS\t99999/PASS_MAX_DAYS\t90/' /etc/login.defs
|
||||
sed -i 's/PASS_MIN_DAYS\t0/PASS_MIN_DAYS\t1/' /etc/login.defs
|
||||
sed -i 's/PASS_MIN_LEN\t5/PASS_MIN_LEN\t8/' /etc/login.defs
|
||||
$RPM && sed -i 's/# minlen/minlen/' /etc/security/pwquality.conf
|
||||
|
||||
$UBUNTU && PAM_FILE=/etc/pam.d/common-password
|
||||
$RHEL && PAM_FILE=/etc/pam.d/password-auth
|
||||
$RPM && PAM_FILE=/etc/pam.d/password-auth
|
||||
sed -i 's/password\t\[success=1 default=ignore\]\tpam_unix\.so obscure sha512/password\t[success=1 default=ignore]\tpam_unix.so obscure sha512 minlen=8/' $PAM_FILE
|
||||
|
||||
# List all the installed packages, for the build log
|
||||
$RPM && (rpm -q --all | sort) || true
|
||||
$UBUNTU && (dpkg --list | sort) || true
|
||||
|
||||
#Update the license file to include UBI 8 instead of UBI 7
|
||||
sed -i 's/v7.0/v8.0/g' /opt/mqm/licenses/non_ibm_license.txt
|
||||
|
||||
# Copy MQ Licenses into the correct location
|
||||
mkdir -p /licenses
|
||||
cp /opt/mqm/licenses/*.txt /licenses/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
© Copyright IBM Corporation 2017, 2018
|
||||
© Copyright IBM Corporation 2017, 2020
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -20,69 +20,20 @@ package command
|
||||
import (
|
||||
"fmt"
|
||||
"os/exec"
|
||||
"os/user"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
// RunCmd runs an OS command. On Linux it waits for the command to
|
||||
// complete and returns the exit status (return code).
|
||||
// Do not use this function to run shell built-ins (like "cd"), because
|
||||
// the error handling works differently
|
||||
func RunCmd(cmd *exec.Cmd) (string, int, error) {
|
||||
// Run the command and wait for completion
|
||||
out, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
// Assert that this is an ExitError
|
||||
exiterr, ok := err.(*exec.ExitError)
|
||||
// If the type assertion was correct, and we're on Linux
|
||||
if ok && runtime.GOOS == "linux" {
|
||||
status, ok := exiterr.Sys().(syscall.WaitStatus)
|
||||
if ok {
|
||||
return string(out), status.ExitStatus(), fmt.Errorf("%v: %v", cmd.Path, err)
|
||||
}
|
||||
}
|
||||
return string(out), -1, err
|
||||
}
|
||||
return string(out), 0, nil
|
||||
}
|
||||
|
||||
// Run runs an OS command. On Linux it waits for the command to
|
||||
// complete and returns the exit status (return code).
|
||||
// Do not use this function to run shell built-ins (like "cd"), because
|
||||
// the error handling works differently
|
||||
func Run(name string, arg ...string) (string, int, error) {
|
||||
// #nosec G204
|
||||
return RunCmd(exec.Command(name, arg...))
|
||||
}
|
||||
|
||||
// RunAsMQM runs the specified command as the mqm user
|
||||
func RunAsMQM(name string, arg ...string) (string, int, error) {
|
||||
// Run the command and wait for completion
|
||||
// #nosec G204
|
||||
cmd := exec.Command(name, arg...)
|
||||
cmd.SysProcAttr = &syscall.SysProcAttr{}
|
||||
uid, gid, err := LookupMQM()
|
||||
out, err := cmd.CombinedOutput()
|
||||
rc := cmd.ProcessState.ExitCode()
|
||||
if err != nil {
|
||||
return "", 0, err
|
||||
return string(out), rc, fmt.Errorf("%v: %v", cmd.Path, err)
|
||||
}
|
||||
cmd.SysProcAttr.Credential = &syscall.Credential{Uid: uint32(uid), Gid: uint32(gid)}
|
||||
return RunCmd(cmd)
|
||||
}
|
||||
|
||||
// LookupMQM looks up the UID & GID of the mqm user
|
||||
func LookupMQM() (int, int, error) {
|
||||
mqm, err := user.Lookup("mqm")
|
||||
if err != nil {
|
||||
return -1, -1, err
|
||||
}
|
||||
mqmUID, err := strconv.Atoi(mqm.Uid)
|
||||
if err != nil {
|
||||
return -1, -1, err
|
||||
}
|
||||
mqmGID, err := strconv.Atoi(mqm.Gid)
|
||||
if err != nil {
|
||||
return -1, -1, err
|
||||
}
|
||||
return mqmUID, mqmGID, nil
|
||||
return string(out), rc, nil
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
package runtime
|
||||
package containerruntime
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
@@ -107,3 +107,22 @@ func GetKernelVersion() (string, error) {
|
||||
func GetMaxFileHandles() (string, error) {
|
||||
return readProc("/proc/sys/fs/file-max")
|
||||
}
|
||||
|
||||
// SupportedFilesystem returns true if the supplied filesystem type is supported for MQ data
|
||||
func SupportedFilesystem(fsType string) bool {
|
||||
switch fsType {
|
||||
case "aufs", "overlayfs", "tmpfs":
|
||||
return false
|
||||
default:
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// ValidMultiInstanceFilesystem returns true if the supplied filesystem type is valid for a multi-instance queue manager
|
||||
func ValidMultiInstanceFilesystem(fsType string) bool {
|
||||
if !SupportedFilesystem(fsType) {
|
||||
return false
|
||||
}
|
||||
// TODO : check for non-shared filesystems & shared filesystems which are known not to work
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
package runtime
|
||||
package containerruntime
|
||||
|
||||
import (
|
||||
"golang.org/x/sys/unix"
|
||||
@@ -113,13 +113,3 @@ func GetFilesystem(path string) (string, error) {
|
||||
}
|
||||
return t, nil
|
||||
}
|
||||
|
||||
// SupportedFilesystem returns true if the supplied filesystem type is supported for MQ data
|
||||
func SupportedFilesystem(fsType string) bool {
|
||||
switch fsType {
|
||||
case "aufs", "overlayfs", "tmpfs":
|
||||
return false
|
||||
default:
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
package runtime
|
||||
package containerruntime
|
||||
|
||||
// Dummy version of this function, only for non-Linux systems.
|
||||
// Having this allows unit tests to be run on other platforms (e.g. macOS)
|
||||
|
||||
61
internal/copy/copy.go
Normal file
61
internal/copy/copy.go
Normal file
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
© Copyright IBM Corporation 2019
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package copy
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"github.com/ibm-messaging/mq-container/internal/filecheck"
|
||||
)
|
||||
|
||||
func CopyFileMode(src, dest string, perm os.FileMode) error {
|
||||
|
||||
err := filecheck.CheckFileSource(src)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to open %s for copy: %v", src, err)
|
||||
}
|
||||
|
||||
// #nosec G304 - filename variable 'src' is checked above to ensure it is valid
|
||||
in, err := os.Open(src)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to open %s for copy: %v", src, err)
|
||||
}
|
||||
// #nosec G307 - local to this function, pose no harm.
|
||||
defer in.Close()
|
||||
|
||||
// #nosec G304 - this func creates based on the input filemode.
|
||||
out, err := os.OpenFile(dest, os.O_CREATE|os.O_WRONLY, perm)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to open %s for copy: %v", dest, err)
|
||||
}
|
||||
// #nosec G307 - local to this function, pose no harm.
|
||||
defer out.Close()
|
||||
|
||||
_, err = io.Copy(out, in)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = out.Close()
|
||||
return err
|
||||
}
|
||||
|
||||
// CopyFile copies the specified file
|
||||
func CopyFile(src, dest string) error {
|
||||
return CopyFileMode(src, dest, 0770)
|
||||
}
|
||||
@@ -1,7 +1,5 @@
|
||||
// +build !mqdev
|
||||
|
||||
/*
|
||||
© Copyright IBM Corporation 2018
|
||||
© Copyright IBM Corporation 2019
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -15,8 +13,25 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
package main
|
||||
|
||||
func postInit(name string) error {
|
||||
package filecheck
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// CheckFileSource checks the filename is valid
|
||||
func CheckFileSource(fileName string) error {
|
||||
|
||||
absFile, _ := filepath.Abs(fileName)
|
||||
|
||||
prefixes := []string{"bin", "boot", "dev", "lib", "lib64", "proc", "sbin", "sys"}
|
||||
for _, prefix := range prefixes {
|
||||
if strings.HasPrefix(absFile, filepath.Join("/", prefix)) {
|
||||
return fmt.Errorf("Filename resolves to invalid path '%v'", absFile)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
40
internal/filecheck/filecheck_test.go
Normal file
40
internal/filecheck/filecheck_test.go
Normal file
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
© Copyright IBM Corporation 2019
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package filecheck
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestCheckFileSource(t *testing.T) {
|
||||
|
||||
invalidFilenames := []string{"/bin", "/boot", "/dev", "/lib", "/lib64", "/proc", "/sbin", "/sys", "/bin/myfile", "/boot/mydir/myfile", "/var/../dev", "/var/../lib/myfile"}
|
||||
for _, invalidFilename := range invalidFilenames {
|
||||
err := CheckFileSource(invalidFilename)
|
||||
if err == nil {
|
||||
t.Errorf("Expected to receive an error for filename '%v'", invalidFilename)
|
||||
}
|
||||
}
|
||||
|
||||
validFilenames := []string{"/var", "/mydir/dev", "/mydir/dev/myfile"}
|
||||
for _, validFilename := range validFilenames {
|
||||
err := CheckFileSource(validFilename)
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error received: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
68
internal/ha/ha.go
Normal file
68
internal/ha/ha.go
Normal file
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
© Copyright IBM Corporation 2020, 2021
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// Package ha contains code for high availability
|
||||
package ha
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/ibm-messaging/mq-container/internal/mqtemplate"
|
||||
"github.com/ibm-messaging/mq-container/internal/tls"
|
||||
"github.com/ibm-messaging/mq-container/pkg/logger"
|
||||
)
|
||||
|
||||
// ConfigureNativeHA configures native high availability
|
||||
func ConfigureNativeHA(log *logger.Logger) error {
|
||||
|
||||
file := "/etc/mqm/native-ha.ini"
|
||||
templateFile := file + ".tpl"
|
||||
|
||||
templateMap := map[string]string{}
|
||||
templateMap["Name"] = os.Getenv("HOSTNAME")
|
||||
templateMap["NativeHAInstance0_Name"] = os.Getenv("MQ_NATIVE_HA_INSTANCE_0_NAME")
|
||||
templateMap["NativeHAInstance1_Name"] = os.Getenv("MQ_NATIVE_HA_INSTANCE_1_NAME")
|
||||
templateMap["NativeHAInstance2_Name"] = os.Getenv("MQ_NATIVE_HA_INSTANCE_2_NAME")
|
||||
templateMap["NativeHAInstance0_ReplicationAddress"] = os.Getenv("MQ_NATIVE_HA_INSTANCE_0_REPLICATION_ADDRESS")
|
||||
templateMap["NativeHAInstance1_ReplicationAddress"] = os.Getenv("MQ_NATIVE_HA_INSTANCE_1_REPLICATION_ADDRESS")
|
||||
templateMap["NativeHAInstance2_ReplicationAddress"] = os.Getenv("MQ_NATIVE_HA_INSTANCE_2_REPLICATION_ADDRESS")
|
||||
|
||||
if os.Getenv("MQ_NATIVE_HA_TLS") == "true" {
|
||||
keyLabel, _, _, err := tls.ConfigureHATLSKeystore()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
templateMap["CertificateLabel"] = keyLabel
|
||||
|
||||
keyRepository, ok := os.LookupEnv("MQ_NATIVE_HA_KEY_REPOSITORY")
|
||||
if !ok {
|
||||
keyRepository = "/run/runmqserver/ha/tls/key"
|
||||
}
|
||||
templateMap["KeyRepository"] = keyRepository
|
||||
|
||||
cipherSpec, ok := os.LookupEnv("MQ_NATIVE_HA_CIPHERSPEC")
|
||||
if ok {
|
||||
templateMap["CipherSpec"] = cipherSpec
|
||||
}
|
||||
}
|
||||
|
||||
err := mqtemplate.ProcessTemplateFile(templateFile, file, templateMap, log)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
113
internal/htpasswd/htpasswd.go
Normal file
113
internal/htpasswd/htpasswd.go
Normal file
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
© Copyright IBM Corporation 2020, 2021
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
//This is a developer only configuration and not recommended for production usage.
|
||||
|
||||
package htpasswd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
||||
type mapHtPasswd map[string]string
|
||||
|
||||
func encryptPassword(password string) (string, error) {
|
||||
passwordBytes, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(passwordBytes), nil
|
||||
}
|
||||
|
||||
// SetPassword sets encrypted password for the user into htpasswd file
|
||||
func SetPassword(user string, password string, isTest bool) error {
|
||||
|
||||
if len(strings.TrimSpace(user)) == 0 || len(strings.TrimSpace(password)) == 0 {
|
||||
return fmt.Errorf("UserId or Password are empty")
|
||||
}
|
||||
|
||||
passwords := mapHtPasswd(map[string]string{})
|
||||
|
||||
// Read the password file
|
||||
err := passwords.ReadHtPasswordFile(isTest)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
pwd, err := encryptPassword(password)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Set the new password
|
||||
passwords[user] = pwd
|
||||
|
||||
// Update the password file
|
||||
return passwords.updateHtPasswordFile(isTest)
|
||||
}
|
||||
|
||||
// GetBytes return the Bytes representation of the htpassword file
|
||||
func (htpfile mapHtPasswd) GetBytes() (passwordBytes []byte) {
|
||||
passwordBytes = []byte{}
|
||||
for name, hash := range htpfile {
|
||||
passwordBytes = append(passwordBytes, []byte(name+":"+hash+"\n")...)
|
||||
}
|
||||
return passwordBytes
|
||||
}
|
||||
|
||||
// ReadHtPasswordFile parses the htpasswd file
|
||||
func (htpfile mapHtPasswd) ReadHtPasswordFile(isTest bool) error {
|
||||
|
||||
file := "/etc/mqm/mq.htpasswd"
|
||||
if isTest {
|
||||
file = "my.htpasswd"
|
||||
}
|
||||
|
||||
pwdsBytes, err := ioutil.ReadFile(file)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
lines := strings.Split(string(pwdsBytes), "\n")
|
||||
for _, line := range lines {
|
||||
line = strings.TrimSpace(line)
|
||||
if len(line) == 0 {
|
||||
continue
|
||||
}
|
||||
parts := strings.Split(line, ":")
|
||||
if len(parts) != 2 {
|
||||
continue
|
||||
}
|
||||
for i, part := range parts {
|
||||
parts[i] = strings.TrimSpace(part)
|
||||
}
|
||||
htpfile[parts[0]] = parts[1]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (htpfile mapHtPasswd) updateHtPasswordFile(isTest bool) error {
|
||||
|
||||
file := "/etc/mqm/mq.htpasswd"
|
||||
if isTest {
|
||||
file = "my.htpasswd"
|
||||
}
|
||||
// #nosec G306 - its a read by owner/s group, and pose no harm.
|
||||
return ioutil.WriteFile(file, htpfile.GetBytes(), 0660)
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
© Copyright IBM Corporation 2018
|
||||
© Copyright IBM Corporation 2018, 2020
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -13,12 +13,15 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
package main
|
||||
|
||||
// Package keystore contains code to create and update keystores
|
||||
package keystore
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
@@ -53,13 +56,22 @@ func NewCMSKeyStore(filename, password string) *KeyStore {
|
||||
}
|
||||
}
|
||||
|
||||
// NewPKCS12KeyStore creates a new PKCS12 Key Store, managed by the runmqakm command
|
||||
func NewPKCS12KeyStore(filename, password string) *KeyStore {
|
||||
return &KeyStore{
|
||||
Filename: filename,
|
||||
Password: password,
|
||||
keyStoreType: "p12",
|
||||
command: "/opt/mqm/bin/runmqakm",
|
||||
}
|
||||
}
|
||||
|
||||
// Create a key store, if it doesn't already exist
|
||||
func (ks *KeyStore) Create() error {
|
||||
_, err := os.Stat(ks.Filename)
|
||||
if err == nil {
|
||||
// Keystore already exists so we should refresh it by deleting it.
|
||||
extension := filepath.Ext(ks.Filename)
|
||||
log.Debugf("Refreshing keystore: %v", ks.Filename)
|
||||
if ks.keyStoreType == "cms" {
|
||||
// Only delete these when we are refreshing the kdb keystore
|
||||
stashFile := ks.Filename[0:len(ks.Filename)-len(extension)] + ".sth"
|
||||
@@ -67,23 +79,19 @@ func (ks *KeyStore) Create() error {
|
||||
crlFile := ks.Filename[0:len(ks.Filename)-len(extension)] + ".crl"
|
||||
err = os.Remove(stashFile)
|
||||
if err != nil {
|
||||
log.Errorf("Error removing %s: %v", stashFile, err)
|
||||
return err
|
||||
}
|
||||
err = os.Remove(rdbFile)
|
||||
if err != nil {
|
||||
log.Errorf("Error removing %s: %v", rdbFile, err)
|
||||
return err
|
||||
}
|
||||
err = os.Remove(crlFile)
|
||||
if err != nil {
|
||||
log.Errorf("Error removing %s: %v", crlFile, err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
err = os.Remove(ks.Filename)
|
||||
if err != nil {
|
||||
log.Errorf("Error removing %s: %v", ks.Filename, err)
|
||||
return err
|
||||
}
|
||||
} else if !os.IsNotExist(err) {
|
||||
@@ -97,16 +105,6 @@ func (ks *KeyStore) Create() error {
|
||||
return fmt.Errorf("error running \"%v -keydb -create\": %v %s", ks.command, err, out)
|
||||
}
|
||||
|
||||
mqmUID, mqmGID, err := command.LookupMQM()
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return err
|
||||
}
|
||||
err = os.Chown(ks.Filename, mqmUID, mqmGID)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -114,7 +112,6 @@ func (ks *KeyStore) Create() error {
|
||||
func (ks *KeyStore) CreateStash() error {
|
||||
extension := filepath.Ext(ks.Filename)
|
||||
stashFile := ks.Filename[0:len(ks.Filename)-len(extension)] + ".sth"
|
||||
log.Debugf("TLS stash file: %v", stashFile)
|
||||
_, err := os.Stat(stashFile)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
@@ -125,16 +122,6 @@ func (ks *KeyStore) CreateStash() error {
|
||||
}
|
||||
return err
|
||||
}
|
||||
mqmUID, mqmGID, err := command.LookupMQM()
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return err
|
||||
}
|
||||
err = os.Chown(stashFile, mqmUID, mqmGID)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -147,6 +134,33 @@ func (ks *KeyStore) Import(inputFile, password string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// CreateSelfSignedCertificate creates a self-signed certificate in the keystore
|
||||
func (ks *KeyStore) CreateSelfSignedCertificate(label, dn, hostname string) error {
|
||||
out, _, err := command.Run(ks.command, "-cert", "-create", "-db", ks.Filename, "-pw", ks.Password, "-label", label, "-dn", dn, "-san_dnsname", hostname, "-size 2048 -sig_alg sha256 -eku serverAuth")
|
||||
if err != nil {
|
||||
return fmt.Errorf("error running \"%v -cert -create\": %v %s", ks.command, err, out)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Add adds a CA certificate to the keystore
|
||||
func (ks *KeyStore) Add(inputFile, label string) error {
|
||||
out, _, err := command.Run(ks.command, "-cert", "-add", "-db", ks.Filename, "-type", ks.keyStoreType, "-pw", ks.Password, "-file", inputFile, "-label", label)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error running \"%v -cert -add\": %v %s", ks.command, err, out)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Add adds a CA certificate to the keystore
|
||||
func (ks *KeyStore) AddNoLabel(inputFile string) error {
|
||||
out, _, err := command.Run(ks.command, "-cert", "-add", "-db", ks.Filename, "-type", ks.keyStoreType, "-pw", ks.Password, "-file", inputFile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error running \"%v -cert -add\": %v %s", ks.command, err, out)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetCertificateLabels returns the labels of all certificates in the key store
|
||||
func (ks *KeyStore) GetCertificateLabels() ([]string, error) {
|
||||
out, _, err := command.Run(ks.command, "-cert", "-list", "-type", ks.keyStoreType, "-db", ks.Filename, "-pw", ks.Password)
|
||||
@@ -171,9 +185,44 @@ func (ks *KeyStore) GetCertificateLabels() ([]string, error) {
|
||||
|
||||
// RenameCertificate renames the specified certificate
|
||||
func (ks *KeyStore) RenameCertificate(from, to string) error {
|
||||
out, _, err := command.Run(ks.command, "-cert", "-rename", "-db", ks.Filename, "-pw", ks.Password, "-label", from, "-new_label", to)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error running \"%v -cert -rename\": %v %s", ks.command, err, out)
|
||||
if ks.command == "/opt/mqm/bin/runmqakm" {
|
||||
// runmqakm can't handle certs with ' in them so just use capicmd
|
||||
// Overriding gosec here as this function is in an internal package and only callable by our internal functions.
|
||||
// #nosec G204
|
||||
cmd := exec.Command("/opt/mqm/gskit8/bin/gsk8capicmd_64", "-cert", "-rename", "-db", ks.Filename, "-pw", ks.Password, "-label", from, "-new_label", to)
|
||||
cmd.Env = append(os.Environ(), "LD_LIBRARY_PATH=/opt/mqm/gskit8/lib64/:/opt/mqm/gskit8/lib")
|
||||
out, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
return fmt.Errorf("error running \"%v -cert -rename\": %v %s", "/opt/mqm/gskit8/bin/gsk8capicmd_64", err, out)
|
||||
}
|
||||
} else {
|
||||
out, _, err := command.Run(ks.command, "-cert", "-rename", "-db", ks.Filename, "-pw", ks.Password, "-label", from, "-new_label", to)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error running \"%v -cert -rename\": %v %s", ks.command, err, out)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ListAllCertificates Lists all certificates in the keystore
|
||||
func (ks *KeyStore) ListAllCertificates() ([]string, error) {
|
||||
out, _, err := command.Run(ks.command, "-cert", "-list", "-type", ks.keyStoreType, "-db", ks.Filename, "-pw", ks.Password)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error running \"%v -cert -list\": %v %s", ks.command, err, out)
|
||||
}
|
||||
scanner := bufio.NewScanner(strings.NewReader(out))
|
||||
var labels []string
|
||||
for scanner.Scan() {
|
||||
s := scanner.Text()
|
||||
if strings.HasPrefix(s, "-") || strings.HasPrefix(s, "*-") || strings.HasPrefix(s, "!") {
|
||||
s := strings.TrimLeft(s, "-*!")
|
||||
labels = append(labels, strings.TrimSpace(s))
|
||||
}
|
||||
}
|
||||
err = scanner.Err()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return labels, nil
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
© Copyright IBM Corporation 2018
|
||||
© Copyright IBM Corporation 2018, 2019
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -18,7 +18,7 @@ limitations under the License.
|
||||
package metrics
|
||||
|
||||
import (
|
||||
"github.com/ibm-messaging/mq-container/internal/logger"
|
||||
"github.com/ibm-messaging/mq-container/pkg/logger"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
)
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
© Copyright IBM Corporation 2018
|
||||
© Copyright IBM Corporation 2018, 2019
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -23,8 +23,10 @@ import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/ibm-messaging/mq-container/internal/logger"
|
||||
"github.com/ibm-messaging/mq-container/internal/ready"
|
||||
"github.com/ibm-messaging/mq-container/pkg/logger"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -33,12 +35,23 @@ const (
|
||||
|
||||
var (
|
||||
metricsEnabled = false
|
||||
// #nosec G112 - this needs investigation to find reasonable timeout.
|
||||
// git-issue 233 to cover this..
|
||||
metricsServer = &http.Server{Addr: ":" + defaultPort}
|
||||
)
|
||||
|
||||
// GatherMetrics gathers metrics for the queue manager
|
||||
func GatherMetrics(qmName string, log *logger.Logger) {
|
||||
|
||||
// If running in standby mode - wait until the queue manager becomes active
|
||||
for {
|
||||
active, _ := ready.IsRunningAsActiveQM(qmName)
|
||||
if active {
|
||||
break
|
||||
}
|
||||
time.Sleep(requestTimeout * time.Second)
|
||||
}
|
||||
|
||||
metricsEnabled = true
|
||||
|
||||
err := startMetricsGathering(qmName, log)
|
||||
@@ -73,7 +86,7 @@ func startMetricsGathering(qmName string, log *logger.Logger) error {
|
||||
}
|
||||
|
||||
// Setup HTTP server to handle requests from Prometheus
|
||||
http.Handle("/metrics", prometheus.Handler())
|
||||
http.Handle("/metrics", promhttp.Handler())
|
||||
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(200)
|
||||
// #nosec G104
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
© Copyright IBM Corporation 2018
|
||||
© Copyright IBM Corporation 2018, 2019
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -22,7 +22,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/ibm-messaging/mq-container/internal/logger"
|
||||
"github.com/ibm-messaging/mq-container/pkg/logger"
|
||||
"github.com/ibm-messaging/mq-golang/ibmmq"
|
||||
"github.com/ibm-messaging/mq-golang/mqmetric"
|
||||
)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
© Copyright IBM Corporation 2018
|
||||
© Copyright IBM Corporation 2018, 2019
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -19,7 +19,7 @@ import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/ibm-messaging/mq-container/internal/logger"
|
||||
"github.com/ibm-messaging/mq-container/pkg/logger"
|
||||
"github.com/ibm-messaging/mq-golang/mqmetric"
|
||||
)
|
||||
|
||||
|
||||
264
internal/mqscredact/mqscredact.go
Normal file
264
internal/mqscredact/mqscredact.go
Normal file
@@ -0,0 +1,264 @@
|
||||
/*
|
||||
© Copyright IBM Corporation 2019
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package mqscredact
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"io"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
/* List of sensitive MQ Parameters */
|
||||
var sensitiveParameters = []string{"LDAPPWD", "PASSWORD", "SSLCRYP"}
|
||||
|
||||
// redactionString is what sensitive paramters will be replaced with
|
||||
const redactionString = "(*********)"
|
||||
|
||||
func findEndOfParamterString(stringDenoter rune, r *bufio.Reader) string {
|
||||
parameter := ""
|
||||
for {
|
||||
char, _, err := r.ReadRune()
|
||||
if err != nil {
|
||||
return parameter
|
||||
}
|
||||
parameter = parameter + string(char)
|
||||
if char == stringDenoter {
|
||||
break
|
||||
} else if char == '\n' {
|
||||
// Check if we're on a comment line
|
||||
NewLineLoop:
|
||||
for {
|
||||
// Look at next character without moving buffer forwards
|
||||
chars, err := r.Peek(1)
|
||||
if err != nil {
|
||||
return parameter
|
||||
}
|
||||
// Check if we're at the beginning of some data.
|
||||
startOutput, _ := regexp.MatchString(`[^:0-9\s]`, string(chars[0]))
|
||||
if startOutput {
|
||||
// We are at the start, check if we're on a comment line
|
||||
if chars[0] == '*' {
|
||||
// found a comment line. go to the next newline chraracter
|
||||
CommentLoop:
|
||||
for {
|
||||
char, _, err = r.ReadRune()
|
||||
if err != nil {
|
||||
return parameter
|
||||
}
|
||||
parameter = parameter + string(char)
|
||||
if char == '\n' {
|
||||
break CommentLoop
|
||||
}
|
||||
}
|
||||
// Go round again as we're now on a new line
|
||||
continue NewLineLoop
|
||||
}
|
||||
// We've checked for comment and it isn't a comment line so break without moving buffer forwards
|
||||
break NewLineLoop
|
||||
}
|
||||
// Move the buffer forward and try again
|
||||
char, _, _ = r.ReadRune()
|
||||
parameter = parameter + string(char)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return parameter
|
||||
}
|
||||
|
||||
// getParameterString reads from r in order to find the end of the MQSC Parameter value. This is enclosed in ( ).
|
||||
// This function will return what it finds and will increment the reader pointer along as it goes.
|
||||
func getParameterString(r *bufio.Reader) string {
|
||||
// Add the ( in as it will have been dropped before.
|
||||
parameter := "("
|
||||
Loop:
|
||||
for {
|
||||
char, _, err := r.ReadRune()
|
||||
if err != nil {
|
||||
return parameter
|
||||
}
|
||||
|
||||
parameter = parameter + string(char)
|
||||
|
||||
switch char {
|
||||
case ')':
|
||||
break Loop
|
||||
// TODO: Duplicate code..
|
||||
case '\'', '"':
|
||||
parameter = parameter + findEndOfParamterString(char, r)
|
||||
}
|
||||
}
|
||||
|
||||
return parameter
|
||||
}
|
||||
|
||||
func resetAllParameters(currentVerb, originalString *string, lineContinuation, foundGap, parameterNext, redacting, checkComment *bool) {
|
||||
*currentVerb = ""
|
||||
*originalString = ""
|
||||
*lineContinuation = false
|
||||
*foundGap = false
|
||||
*parameterNext = false
|
||||
*redacting = false
|
||||
*checkComment = true
|
||||
}
|
||||
|
||||
// Redact is the main function for redacting sensitive parameters in MQSC strings
|
||||
// It accepts a string and redacts sensitive paramters such as LDAPPWD or PASSWORD
|
||||
func Redact(out string) (string, error) {
|
||||
out = strings.TrimSpace(out)
|
||||
var returnStr, currentVerb, originalString string
|
||||
var lineContinuation, foundGap, parameterNext, redacting, checkComment bool
|
||||
newline := true
|
||||
resetAllParameters(¤tVerb, &originalString, &lineContinuation, &foundGap, ¶meterNext, &redacting, &checkComment)
|
||||
r := bufio.NewReader(strings.NewReader(out))
|
||||
|
||||
MainLoop:
|
||||
for {
|
||||
// We have found a opening ( so use special parameter parsing
|
||||
if parameterNext {
|
||||
parameterStr := getParameterString(r)
|
||||
if !redacting {
|
||||
returnStr = returnStr + parameterStr
|
||||
} else {
|
||||
returnStr = returnStr + redactionString
|
||||
}
|
||||
|
||||
resetAllParameters(¤tVerb, &originalString, &lineContinuation, &foundGap, ¶meterNext, &redacting, &checkComment)
|
||||
}
|
||||
|
||||
// Loop round getting hte next parameter
|
||||
char, _, err := r.ReadRune()
|
||||
if err == io.EOF {
|
||||
if originalString != "" {
|
||||
returnStr = returnStr + originalString
|
||||
}
|
||||
break
|
||||
} else if err != nil {
|
||||
return returnStr, err
|
||||
}
|
||||
|
||||
/* We need to push forward until we find a non-whitespace, digit or colon character */
|
||||
if newline {
|
||||
startOutput, _ := regexp.MatchString(`[^:0-9\s]`, string(char))
|
||||
if !startOutput {
|
||||
originalString = originalString + string(char)
|
||||
continue MainLoop
|
||||
}
|
||||
newline = false
|
||||
}
|
||||
|
||||
switch char {
|
||||
// Found a line continuation character
|
||||
case '+', '-':
|
||||
lineContinuation = true
|
||||
foundGap = false
|
||||
originalString = originalString + string(char)
|
||||
continue MainLoop
|
||||
|
||||
// Found whitespace/new line
|
||||
case '\n':
|
||||
checkComment = true
|
||||
newline = true
|
||||
fallthrough
|
||||
case '\t', '\r', ' ':
|
||||
if !lineContinuation {
|
||||
foundGap = true
|
||||
}
|
||||
originalString = originalString + string(char)
|
||||
continue MainLoop
|
||||
|
||||
// Found a paramter value
|
||||
case '(':
|
||||
parameterNext = true
|
||||
/* Do not continue as we need to do some checks */
|
||||
|
||||
// Found a comment, parse in a special manner
|
||||
case '*':
|
||||
if checkComment {
|
||||
originalString = originalString + string(char)
|
||||
// Loop round until we find the new line character that marks the end of the comment
|
||||
CommentLoop:
|
||||
for {
|
||||
char, _, err := r.ReadRune()
|
||||
if err == io.EOF {
|
||||
if originalString != "" {
|
||||
returnStr = returnStr + originalString
|
||||
}
|
||||
break MainLoop
|
||||
} else if err != nil {
|
||||
return returnStr, err
|
||||
}
|
||||
originalString = originalString + string(char)
|
||||
|
||||
if char == '\n' {
|
||||
break CommentLoop
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//Comment has been read and added to original string, go back to start
|
||||
checkComment = true
|
||||
newline = true
|
||||
continue MainLoop
|
||||
}
|
||||
/* Do not continue as we need to do some checks */
|
||||
|
||||
} //end of switch
|
||||
|
||||
checkComment = false
|
||||
|
||||
if lineContinuation {
|
||||
lineContinuation = false
|
||||
}
|
||||
if foundGap || parameterNext {
|
||||
// we've completed an parameter so check whether it is sensitive
|
||||
currentVerb = strings.ToUpper(currentVerb)
|
||||
|
||||
if isSensitiveCommand(currentVerb) {
|
||||
redacting = true
|
||||
}
|
||||
|
||||
// Add the unedited string to the return string
|
||||
returnStr = returnStr + originalString
|
||||
|
||||
//reset some of the parameters
|
||||
originalString = ""
|
||||
currentVerb = ""
|
||||
foundGap = false
|
||||
lineContinuation = false
|
||||
}
|
||||
|
||||
originalString = originalString + string(char)
|
||||
currentVerb = currentVerb + string(char)
|
||||
}
|
||||
|
||||
return returnStr, nil
|
||||
}
|
||||
|
||||
// isSensitiveCommand checks whether the given string contains a sensitive parameter.
|
||||
// We use contains here because we can't determine whether a line continuation seperates
|
||||
// parts of a parameter or two different parameters.
|
||||
func isSensitiveCommand(command string) bool {
|
||||
for _, v := range sensitiveParameters {
|
||||
if strings.Contains(command, v) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
171
internal/mqscredact/mqscredact_test.go
Normal file
171
internal/mqscredact/mqscredact_test.go
Normal file
@@ -0,0 +1,171 @@
|
||||
/*
|
||||
© Copyright IBM Corporation 2019
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
package mqscredact
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
const passwordString = passwordHalf1 + passwordHalf2
|
||||
const passwordHalf1 = "hippo"
|
||||
const passwordHalf2 = "123456"
|
||||
|
||||
var testStrings = [...]string{
|
||||
"DEFINE AUTHINFO(TEST) AUTHTYPE(IDPWLDAP) LDAPPWD('" + passwordString + "')",
|
||||
"DEFINE AUTHINFO(TEST) AUTHTYPE(IDPWLDAP) LDAPPWD(\"" + passwordString + "\")",
|
||||
"DEFINE AUTHINFO(TEST) AUTHTYPE(IDPWLDAP) LDAPPWD ('" + passwordString + "')",
|
||||
"DEFINE AUTHINFO(TEST) AUTHTYPE(IDPWLDAP) LDAPPWD\t\t('" + passwordString + "')",
|
||||
"DEFINE AUTHINFO(TEST) AUTHTYPE(IDPWLDAP) ldappwd('" + passwordString + "')",
|
||||
"DEFINE AUTHINFO(TEST) AUTHTYPE(IDPWLDAP) LdApPwD('" + passwordString + "')",
|
||||
"DEFINE CHANNEL(CHL) CHLTYPE(SOMETHING) PASSWORD('" + passwordString + "')",
|
||||
"DEFINE CHANNEL(CHL) CHLTYPE(SOMETHING) PASSWORD(\"" + passwordString + "\")",
|
||||
"DEFINE CHANNEL(CHL) CHLTYPE(SOMETHING) PASSWORD ('" + passwordString + "')",
|
||||
"DEFINE CHANNEL(CHL) CHLTYPE(SOMETHING) PASSWORD\t\t('" + passwordString + "')",
|
||||
"DEFINE CHANNEL(CHL) CHLTYPE(SOMETHING) password('" + passwordString + "')",
|
||||
"DEFINE CHANNEL(CHL) CHLTYPE(SOMETHING) pAsSwOrD('" + passwordString + "')",
|
||||
"ALTER QMGR SSLCRYP('" + passwordString + "')",
|
||||
"ALTER QMGR SSLCRYP(\"" + passwordString + "\")",
|
||||
"ALTER QMGR SSLCRYP ('" + passwordString + "')",
|
||||
"ALTER QMGR SSLCRYP\t\t('" + passwordString + "')",
|
||||
"ALTER QMGR sslcryp('" + passwordString + "')",
|
||||
"ALTER QMGR sslCRYP('" + passwordString + "')",
|
||||
|
||||
// Line continuation ones
|
||||
"DEFINE AUTHINFO(TEST) AUTHTYPE(IDPWLDAP) LDAPPWD(\"" + passwordHalf1 + "+\n " + passwordHalf2 + "\")",
|
||||
"DEFINE AUTHINFO(TEST) AUTHTYPE(IDPWLDAP) LDAPPWD(\"" + passwordHalf1 + "+\n\t" + passwordHalf2 + "\")",
|
||||
"DEFINE AUTHINFO(TEST) AUTHTYPE(IDPWLDAP) LDAPPWD(\"" + passwordHalf1 + "+\n\t " + passwordHalf2 + "\")",
|
||||
"DEFINE AUTHINFO(TEST) AUTHTYPE(IDPWLDAP) LDAPPWD('" + passwordHalf1 + "+\n " + passwordHalf2 + "')",
|
||||
"DEFINE AUTHINFO(TEST) AUTHTYPE(IDPWLDAP) LDAPPWD('" + passwordHalf1 + "+\n\t" + passwordHalf2 + "')",
|
||||
"DEFINE AUTHINFO(TEST) AUTHTYPE(IDPWLDAP) LDAPPWD('" + passwordHalf1 + "+\n\t " + passwordHalf2 + "')",
|
||||
"DEFINE AUTHINFO(TEST) AUTHTYPE(IDPWLDAP) LDAPPWD(\"" + passwordHalf1 + "-\n" + passwordHalf2 + "\")",
|
||||
"DEFINE AUTHINFO(TEST) AUTHTYPE(IDPWLDAP) LDAPPWD('" + passwordHalf1 + "-\n" + passwordHalf2 + "')",
|
||||
"DEFINE AUTHINFO(TEST) AUTHTYPE(IDPWLDAP) LDAPPWD(\"" + passwordHalf1 + "+ \n " + passwordHalf2 + "\")",
|
||||
"DEFINE AUTHINFO(TEST) AUTHTYPE(IDPWLDAP) LDAPPWD(\"" + passwordHalf1 + "+\t\n " + passwordHalf2 + "\")",
|
||||
"DEFINE AUTHINFO(TEST) AUTHTYPE(IDPWLDAP) LDAPPWD(\"" + passwordHalf1 + "- \n" + passwordHalf2 + "\")",
|
||||
"DEFINE AUTHINFO(TEST) AUTHTYPE(IDPWLDAP) LDAPPWD(\"" + passwordHalf1 + "-\t\n" + passwordHalf2 + "\")",
|
||||
|
||||
"DEFINE CHANNEL(CHL) CHLTYPE(SOMETHING) PASSWORD(\"" + passwordHalf1 + "+\n " + passwordHalf2 + "\")",
|
||||
|
||||
"ALTER QMGR SSLCRYP(\"" + passwordHalf1 + "+\n " + passwordHalf2 + "\")",
|
||||
|
||||
//edge cases
|
||||
"ALTER QMGR SSLCRYP(\"" + passwordHalf1 + "+\n 123+\n 456\")",
|
||||
"ALTER QMGR SSLCRYP(\"" + passwordHalf1 + "-\n123-\n456\")",
|
||||
|
||||
"ALTER QMGR SSLCRYP(\"" + passwordHalf1 + "+\n 1+\n 2+\n 3+\n 4+\n 5+\n 6\")",
|
||||
"ALTER QMGR SSLCRYP(\"" + passwordHalf1 + "-\n1-\n2-\n3-\n4-\n5-\n6\")",
|
||||
|
||||
"ALTER QMGR SSLCRYP + \n (\"" + passwordHalf1 + "+\n 1+\n 2+\n 3+\n 4+\n 5+\n 6\")",
|
||||
"ALTER QMGR SSLCRYP - \n(\"" + passwordHalf1 + "-\n1-\n2-\n3-\n4-\n5-\n6\")",
|
||||
|
||||
"ALTER QMGR SSL + \n CRYP(\"" + passwordHalf1 + "+\n 1+\n 2+\n 3+\n 4+\n 5+\n 6\")",
|
||||
"ALTER QMGR SSL - \nCRYP(\"" + passwordHalf1 + "-\n1-\n2-\n3-\n4-\n5-\n6\")",
|
||||
|
||||
"ALTER QMGR + \n SSL +\n CRYP(\"" + passwordHalf1 + "+\n 1+\n 2+\n 3+\n 4+\n 5+\n 6\") +\n TEST(1234)",
|
||||
"ALTER QMGR -\nSSL -\nCRYP(\"" + passwordHalf1 + "-\n1-\n2-\n3-\n4-\n5-\n6\") -\nTEST(1234)",
|
||||
|
||||
"ALTER QMGR +\n * COMMENT\n SSL +\n * COMMENT IN MIDDLE\n CRYP('" + passwordString + "')",
|
||||
|
||||
" 1: ALTER CHANNEL(TEST2) CHLTYPE(SDR) PASS+\n : *test comment\n : WORD('" + passwordString + "')",
|
||||
" 2: ALTER CHANNEL(TEST3) CHLTYPE(SDR) PASSWORD('" + passwordHalf1 + "-\n*commentinmiddle with ' \n" + passwordHalf2 + "')",
|
||||
" 3: ALTER CHANNEL(TEST3) CHLTYPE(SDR) PASSWORD('" + passwordHalf1 + "-\n*commentinmiddle with ') \n" + passwordHalf2 + "')",
|
||||
}
|
||||
|
||||
var expected = [...]string{
|
||||
"DEFINE AUTHINFO(TEST) AUTHTYPE(IDPWLDAP) LDAPPWD" + redactionString,
|
||||
"DEFINE AUTHINFO(TEST) AUTHTYPE(IDPWLDAP) LDAPPWD" + redactionString,
|
||||
"DEFINE AUTHINFO(TEST) AUTHTYPE(IDPWLDAP) LDAPPWD " + redactionString,
|
||||
"DEFINE AUTHINFO(TEST) AUTHTYPE(IDPWLDAP) LDAPPWD\t\t" + redactionString,
|
||||
"DEFINE AUTHINFO(TEST) AUTHTYPE(IDPWLDAP) ldappwd" + redactionString,
|
||||
"DEFINE AUTHINFO(TEST) AUTHTYPE(IDPWLDAP) LdApPwD" + redactionString,
|
||||
"DEFINE CHANNEL(CHL) CHLTYPE(SOMETHING) PASSWORD" + redactionString,
|
||||
"DEFINE CHANNEL(CHL) CHLTYPE(SOMETHING) PASSWORD" + redactionString,
|
||||
"DEFINE CHANNEL(CHL) CHLTYPE(SOMETHING) PASSWORD " + redactionString,
|
||||
"DEFINE CHANNEL(CHL) CHLTYPE(SOMETHING) PASSWORD\t\t" + redactionString,
|
||||
"DEFINE CHANNEL(CHL) CHLTYPE(SOMETHING) password" + redactionString,
|
||||
"DEFINE CHANNEL(CHL) CHLTYPE(SOMETHING) pAsSwOrD" + redactionString,
|
||||
"ALTER QMGR SSLCRYP" + redactionString,
|
||||
"ALTER QMGR SSLCRYP" + redactionString,
|
||||
"ALTER QMGR SSLCRYP " + redactionString,
|
||||
"ALTER QMGR SSLCRYP\t\t" + redactionString,
|
||||
"ALTER QMGR sslcryp" + redactionString,
|
||||
"ALTER QMGR sslCRYP" + redactionString,
|
||||
|
||||
// Line continuation ones
|
||||
"DEFINE AUTHINFO(TEST) AUTHTYPE(IDPWLDAP) LDAPPWD" + redactionString,
|
||||
"DEFINE AUTHINFO(TEST) AUTHTYPE(IDPWLDAP) LDAPPWD" + redactionString,
|
||||
"DEFINE AUTHINFO(TEST) AUTHTYPE(IDPWLDAP) LDAPPWD" + redactionString,
|
||||
"DEFINE AUTHINFO(TEST) AUTHTYPE(IDPWLDAP) LDAPPWD" + redactionString,
|
||||
"DEFINE AUTHINFO(TEST) AUTHTYPE(IDPWLDAP) LDAPPWD" + redactionString,
|
||||
"DEFINE AUTHINFO(TEST) AUTHTYPE(IDPWLDAP) LDAPPWD" + redactionString,
|
||||
"DEFINE AUTHINFO(TEST) AUTHTYPE(IDPWLDAP) LDAPPWD" + redactionString,
|
||||
"DEFINE AUTHINFO(TEST) AUTHTYPE(IDPWLDAP) LDAPPWD" + redactionString,
|
||||
"DEFINE AUTHINFO(TEST) AUTHTYPE(IDPWLDAP) LDAPPWD" + redactionString,
|
||||
"DEFINE AUTHINFO(TEST) AUTHTYPE(IDPWLDAP) LDAPPWD" + redactionString,
|
||||
"DEFINE AUTHINFO(TEST) AUTHTYPE(IDPWLDAP) LDAPPWD" + redactionString,
|
||||
"DEFINE AUTHINFO(TEST) AUTHTYPE(IDPWLDAP) LDAPPWD" + redactionString,
|
||||
|
||||
"DEFINE CHANNEL(CHL) CHLTYPE(SOMETHING) PASSWORD" + redactionString,
|
||||
|
||||
"ALTER QMGR SSLCRYP" + redactionString,
|
||||
|
||||
//edge cases
|
||||
"ALTER QMGR SSLCRYP" + redactionString,
|
||||
"ALTER QMGR SSLCRYP" + redactionString,
|
||||
|
||||
"ALTER QMGR SSLCRYP" + redactionString,
|
||||
"ALTER QMGR SSLCRYP" + redactionString,
|
||||
|
||||
"ALTER QMGR SSLCRYP + \n \t " + redactionString,
|
||||
"ALTER QMGR SSLCRYP - \n " + redactionString,
|
||||
|
||||
"ALTER QMGR SSL + \n CRYP" + redactionString,
|
||||
"ALTER QMGR SSL - \nCRYP" + redactionString,
|
||||
|
||||
"ALTER QMGR + \n SSL +\n CRYP" + redactionString + " +\n TEST(1234)",
|
||||
"ALTER QMGR -\nSSL -\nCRYP" + redactionString + " -\nTEST(1234)",
|
||||
|
||||
"ALTER QMGR +\n * COMMENT\n SSL +\n * COMMENT IN MIDDLE\n CRYP" + redactionString,
|
||||
|
||||
"1: ALTER CHANNEL(TEST2) CHLTYPE(SDR) PASS+\n : *test comment\n : WORD" + redactionString,
|
||||
"2: ALTER CHANNEL(TEST3) CHLTYPE(SDR) PASSWORD" + redactionString,
|
||||
"3: ALTER CHANNEL(TEST3) CHLTYPE(SDR) PASSWORD" + redactionString,
|
||||
}
|
||||
|
||||
// Returns true if the 2 strings are equal ignoring whitespace characters
|
||||
func compareIgnoreWhiteSpace(str1, str2 string) bool {
|
||||
whiteSpaces := [...]string{" ", "\t", "\n", "\r"}
|
||||
for _, w := range whiteSpaces {
|
||||
str1 = strings.Replace(str1, w, "", -1)
|
||||
str2 = strings.Replace(str2, w, "", -1)
|
||||
}
|
||||
|
||||
return str1 == str2
|
||||
}
|
||||
|
||||
func TestAll(t *testing.T) {
|
||||
for i, v := range testStrings {
|
||||
back, _ := Redact(v)
|
||||
if strings.Contains(back, passwordHalf1) || strings.Contains(back, passwordHalf2) || strings.Contains(back, passwordString) {
|
||||
t.Errorf("MAJOR FAIL[%d]: Found an instance of the password. ", i)
|
||||
}
|
||||
|
||||
if !compareIgnoreWhiteSpace(back, expected[i]) {
|
||||
t.Errorf("FAIL[%d]:\nGave :%s\nexpected:%s\ngot :%s", i, v, expected[i], back)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
© Copyright IBM Corporation 2018, 2019
|
||||
© Copyright IBM Corporation 2018, 2020
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -13,19 +13,21 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
package main
|
||||
|
||||
// Package mqtemplate contains code to process template files
|
||||
package mqtemplate
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path"
|
||||
"text/template"
|
||||
|
||||
"github.com/ibm-messaging/mq-container/internal/command"
|
||||
"github.com/ibm-messaging/mq-container/pkg/logger"
|
||||
)
|
||||
|
||||
// processTemplateFile takes a Go templateFile, and processes it with the
|
||||
// ProcessTemplateFile takes a Go templateFile, and processes it with the
|
||||
// supplied data, writing to destFile
|
||||
func processTemplateFile(templateFile, destFile string, data interface{}) error {
|
||||
func ProcessTemplateFile(templateFile, destFile string, data interface{}, log *logger.Logger) error {
|
||||
// Re-configure channel if app password not set
|
||||
t, err := template.ParseFiles(templateFile)
|
||||
if err != nil {
|
||||
@@ -42,37 +44,19 @@ func processTemplateFile(templateFile, destFile string, data interface{}) error
|
||||
log.Error(err)
|
||||
return err
|
||||
}
|
||||
mqmUID, mqmGID, err := command.LookupMQM()
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return err
|
||||
}
|
||||
err = os.Chown(dir, mqmUID, mqmGID)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
}
|
||||
// #nosec G302
|
||||
|
||||
// #nosec G302 G304 G306 - its a read by owner/s group, and pose no harm.
|
||||
f, err := os.OpenFile(destFile, os.O_CREATE|os.O_WRONLY, 0660)
|
||||
// #nosec G307 - local to this function, pose no harm.
|
||||
defer f.Close()
|
||||
err = t.Execute(f, data)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return err
|
||||
}
|
||||
mqmUID, mqmGID, err := command.LookupMQM()
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return err
|
||||
}
|
||||
err = os.Chown(destFile, mqmUID, mqmGID)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
97
internal/mqversion/mqversion.go
Normal file
97
internal/mqversion/mqversion.go
Normal file
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
© Copyright IBM Corporation 2020
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package mqversion
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/ibm-messaging/mq-container/internal/command"
|
||||
)
|
||||
|
||||
// Get will return the current MQ version
|
||||
func Get() (string, error) {
|
||||
mqVersion, _, err := command.Run("dspmqver", "-b", "-f", "2")
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("Error Getting MQ version: %v", err)
|
||||
}
|
||||
return strings.TrimSpace(mqVersion), nil
|
||||
}
|
||||
|
||||
// Compare returns an integer comparing two MQ version strings lexicographically. The result will be 0 if currentVersion==checkVersion, -1 if currentVersion < checkVersion, and +1 if currentVersion > checkVersion
|
||||
func Compare(checkVersion string) (int, error) {
|
||||
currentVersion, err := Get()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
currentVRMF, err := parseVRMF(currentVersion)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
compareVRMF, err := parseVRMF(checkVersion)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("failed to parse compare version: %w", err)
|
||||
}
|
||||
return currentVRMF.compare(*compareVRMF), nil
|
||||
}
|
||||
|
||||
type vrmf [4]int
|
||||
|
||||
func (v vrmf) String() string {
|
||||
return fmt.Sprintf("%d.%d.%d.%d", v[0], v[1], v[2], v[3])
|
||||
}
|
||||
|
||||
func (v vrmf) compare(to vrmf) int {
|
||||
for idx := 0; idx < 4; idx++ {
|
||||
if v[idx] < to[idx] {
|
||||
return -1
|
||||
}
|
||||
if v[idx] > to[idx] {
|
||||
return 1
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func parseVRMF(vrmfString string) (*vrmf, error) {
|
||||
versionParts := strings.Split(vrmfString, ".")
|
||||
if len(versionParts) != 4 {
|
||||
return nil, fmt.Errorf("incorrect number of parts to version string: expected 4, got %d", len(versionParts))
|
||||
}
|
||||
vmrfPartNames := []string{"version", "release", "minor", "fix"}
|
||||
parsed := vrmf{}
|
||||
for idx, value := range versionParts {
|
||||
partName := vmrfPartNames[idx]
|
||||
if value == "" {
|
||||
return nil, fmt.Errorf("empty %s found in VRMF", partName)
|
||||
}
|
||||
val, err := strconv.Atoi(value)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("non-numeric %s found in VRMF", partName)
|
||||
}
|
||||
if val < 0 {
|
||||
return nil, fmt.Errorf("negative %s found in VRMF", partName)
|
||||
}
|
||||
if idx == 0 && val == 0 {
|
||||
return nil, fmt.Errorf("zero value for version not allowed")
|
||||
}
|
||||
parsed[idx] = val
|
||||
}
|
||||
return &parsed, nil
|
||||
}
|
||||
147
internal/mqversion/mqversion_test.go
Normal file
147
internal/mqversion/mqversion_test.go
Normal file
@@ -0,0 +1,147 @@
|
||||
/*
|
||||
© Copyright IBM Corporation 2020
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package mqversion
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestCompareLower(t *testing.T) {
|
||||
checkVersion := "99.99.99.99"
|
||||
mqVersionCheck, err := Compare(checkVersion)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to compare MQ versions: %v", err)
|
||||
}
|
||||
if mqVersionCheck != -1 {
|
||||
t.Errorf("MQ version compare result failed. Expected -1, Got %v", mqVersionCheck)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCompareHigher(t *testing.T) {
|
||||
checkVersion := "1.1.1.1"
|
||||
mqVersionCheck, err := Compare(checkVersion)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to compare MQ versions: %v", err)
|
||||
}
|
||||
if mqVersionCheck != 1 {
|
||||
t.Errorf("MQ version compare result failed. Expected 1, Got %v", mqVersionCheck)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCompareEqual(t *testing.T) {
|
||||
checkVersion, err := Get()
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to get current MQ version: %v", err)
|
||||
}
|
||||
mqVersionCheck, err := Compare(checkVersion)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to compare MQ versions: %v", err)
|
||||
}
|
||||
if mqVersionCheck != 0 {
|
||||
t.Errorf("MQ version compare result failed. Expected 0, Got %v", mqVersionCheck)
|
||||
}
|
||||
}
|
||||
|
||||
func TestVersionValid(t *testing.T) {
|
||||
checkVersion, err := Get()
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to get current MQ version: %v", err)
|
||||
}
|
||||
_, err = parseVRMF(checkVersion)
|
||||
if err != nil {
|
||||
t.Fatalf("Validation of MQ version failed: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidVRMF(t *testing.T) {
|
||||
validVRMFs := map[string]vrmf{
|
||||
"1.0.0.0": {1, 0, 0, 0},
|
||||
"10.0.0.0": {10, 0, 0, 0},
|
||||
"1.10.0.0": {1, 10, 0, 0},
|
||||
"1.0.10.0": {1, 0, 10, 0},
|
||||
"1.0.0.10": {1, 0, 0, 10},
|
||||
"999.998.997.996": {999, 998, 997, 996},
|
||||
}
|
||||
for test, expect := range validVRMFs {
|
||||
t.Run(test, func(t *testing.T) {
|
||||
parsed, err := parseVRMF(test)
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpectedly failed to parse VRMF '%s': %s", test, err.Error())
|
||||
}
|
||||
if *parsed != expect {
|
||||
t.Fatalf("VRMF not parsed as expected. Expected '%v', got '%v'", parsed, expect)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestInvalidVRMF(t *testing.T) {
|
||||
invalidVRMFs := []string{
|
||||
"not-a-number",
|
||||
"9.8.7.string",
|
||||
"0.1.2.3",
|
||||
"1.0.0.-10",
|
||||
}
|
||||
for _, test := range invalidVRMFs {
|
||||
t.Run(test, func(t *testing.T) {
|
||||
parsed, err := parseVRMF(test)
|
||||
if err == nil {
|
||||
t.Fatalf("Expected error when parsing VRMF '%s', but got none. VRMF returned: %v", test, parsed)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCompare(t *testing.T) {
|
||||
tests := []struct {
|
||||
current string
|
||||
compare string
|
||||
expect int
|
||||
}{
|
||||
{"1.0.0.1", "1.0.0.1", 0},
|
||||
{"1.0.0.1", "1.0.0.0", 1},
|
||||
{"1.0.0.1", "1.0.0.2", -1},
|
||||
{"9.9.9.9", "10.0.0.0", -1},
|
||||
{"9.9.9.9", "9.10.0.0", -1},
|
||||
{"9.9.9.9", "9.9.10.0", -1},
|
||||
{"9.9.9.9", "9.9.9.10", -1},
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(fmt.Sprintf("%s-%s", test.current, test.compare), func(t *testing.T) {
|
||||
baseVRMF, err := parseVRMF(test.current)
|
||||
if err != nil {
|
||||
t.Fatalf("Could not parse base version '%s': %s", test.current, err.Error())
|
||||
}
|
||||
compareVRMF, err := parseVRMF(test.compare)
|
||||
if err != nil {
|
||||
t.Fatalf("Could not parse current version '%s': %s", test.current, err.Error())
|
||||
}
|
||||
result := baseVRMF.compare(*compareVRMF)
|
||||
if result != test.expect {
|
||||
t.Fatalf("Expected %d but got %d when comparing '%s' with '%s'", test.expect, result, test.current, test.compare)
|
||||
}
|
||||
if test.expect == 0 {
|
||||
return
|
||||
}
|
||||
resultReversed := compareVRMF.compare(*baseVRMF)
|
||||
if resultReversed != test.expect*-1 {
|
||||
t.Fatalf("Expected %d but got %d when comparing '%s' with '%s'", test.expect*-1, resultReversed, test.compare, test.current)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
© Copyright IBM Corporation 2018
|
||||
© Copyright IBM Corporation 2018, 2019
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -20,6 +20,9 @@ package ready
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/ibm-messaging/mq-container/internal/command"
|
||||
)
|
||||
|
||||
const fileName string = "/run/runmqserver/ready"
|
||||
@@ -50,6 +53,7 @@ func Clear() error {
|
||||
// Set lets any subsequent calls to `CheckReady` know that the queue
|
||||
// manager has finished its configuration step
|
||||
func Set() error {
|
||||
// #nosec G306 - this gives permissions to owner/s group only.
|
||||
return ioutil.WriteFile(fileName, []byte("1"), 0770)
|
||||
}
|
||||
|
||||
@@ -62,3 +66,29 @@ func Check() (bool, error) {
|
||||
}
|
||||
return exists, nil
|
||||
}
|
||||
|
||||
// IsRunningAsActiveQM returns true if the queue manager is running in active mode
|
||||
func IsRunningAsActiveQM(name string) (bool, error) {
|
||||
return isRunningQM(name, "(RUNNING)")
|
||||
}
|
||||
|
||||
// IsRunningAsStandbyQM returns true if the queue manager is running in standby mode
|
||||
func IsRunningAsStandbyQM(name string) (bool, error) {
|
||||
return isRunningQM(name, "(RUNNING AS STANDBY)")
|
||||
}
|
||||
|
||||
// IsRunningAsReplicaQM returns true if the queue manager is running in replica mode
|
||||
func IsRunningAsReplicaQM(name string) (bool, error) {
|
||||
return isRunningQM(name, "(REPLICA)")
|
||||
}
|
||||
|
||||
func isRunningQM(name string, status string) (bool, error) {
|
||||
out, _, err := command.Run("dspmq", "-n", "-m", name)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if strings.Contains(string(out), status) {
|
||||
return true, nil
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
609
internal/tls/tls.go
Normal file
609
internal/tls/tls.go
Normal file
@@ -0,0 +1,609 @@
|
||||
/*
|
||||
© Copyright IBM Corporation 2019, 2023
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
package tls
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
pwr "math/rand"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"crypto/rand"
|
||||
"crypto/sha512"
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
|
||||
pkcs "software.sslmate.com/src/go-pkcs12"
|
||||
|
||||
"github.com/ibm-messaging/mq-container/internal/keystore"
|
||||
"github.com/ibm-messaging/mq-container/internal/mqtemplate"
|
||||
"github.com/ibm-messaging/mq-container/pkg/logger"
|
||||
)
|
||||
|
||||
// cmsKeystoreName is the name of the CMS Keystore
|
||||
const cmsKeystoreName = "key.kdb"
|
||||
|
||||
// p12TruststoreName is the name of the PKCS#12 Truststore
|
||||
const p12TruststoreName = "trust.p12"
|
||||
|
||||
// keystoreDirDefault is the location for the default CMS Keystore & PKCS#12 Truststore
|
||||
const keystoreDirDefault = "/run/runmqserver/tls/"
|
||||
|
||||
// keystoreDirHA is the location for the HA CMS Keystore
|
||||
const keystoreDirHA = "/run/runmqserver/ha/tls/"
|
||||
|
||||
// keyDirDefault is the location of the default keys to import
|
||||
const keyDirDefault = "/etc/mqm/pki/keys"
|
||||
|
||||
// keyDirHA is the location of the HA keys to import
|
||||
const keyDirHA = "/etc/mqm/ha/pki/keys"
|
||||
|
||||
// trustDirDefault is the location of the trust certificates to import
|
||||
const trustDirDefault = "/etc/mqm/pki/trust"
|
||||
|
||||
type KeyStoreData struct {
|
||||
Keystore *keystore.KeyStore
|
||||
Password string
|
||||
TrustedCerts []*pem.Block
|
||||
KnownFingerPrints []string
|
||||
KeyLabels []string
|
||||
}
|
||||
|
||||
type P12KeyFiles struct {
|
||||
Keystores []string
|
||||
Password string
|
||||
}
|
||||
|
||||
type TLSStore struct {
|
||||
Keystore KeyStoreData
|
||||
Truststore KeyStoreData
|
||||
}
|
||||
|
||||
func configureTLSKeystores(keystoreDir, keyDir, trustDir string, p12TruststoreRequired bool) (string, KeyStoreData, KeyStoreData, error) {
|
||||
|
||||
// Create the CMS Keystore & PKCS#12 Truststore (if required)
|
||||
tlsStore, err := generateAllKeystores(keystoreDir, p12TruststoreRequired)
|
||||
if err != nil {
|
||||
return "", tlsStore.Keystore, tlsStore.Truststore, err
|
||||
}
|
||||
|
||||
// Process all keys - add them to the CMS KeyStore
|
||||
keyLabel, err := processKeys(&tlsStore, keystoreDir, keyDir)
|
||||
if err != nil {
|
||||
return "", tlsStore.Keystore, tlsStore.Truststore, err
|
||||
}
|
||||
|
||||
// Process all trust certificates - add them to the CMS KeyStore & PKCS#12 Truststore (if required)
|
||||
err = processTrustCertificates(&tlsStore, trustDir)
|
||||
if err != nil {
|
||||
return "", tlsStore.Keystore, tlsStore.Truststore, err
|
||||
}
|
||||
|
||||
return keyLabel, tlsStore.Keystore, tlsStore.Truststore, err
|
||||
}
|
||||
|
||||
// ConfigureDefaultTLSKeystores configures the CMS Keystore & PKCS#12 Truststore
|
||||
func ConfigureDefaultTLSKeystores() (string, KeyStoreData, KeyStoreData, error) {
|
||||
return configureTLSKeystores(keystoreDirDefault, keyDirDefault, trustDirDefault, true)
|
||||
}
|
||||
|
||||
// ConfigureHATLSKeystore configures the CMS Keystore & PKCS#12 Truststore
|
||||
func ConfigureHATLSKeystore() (string, KeyStoreData, KeyStoreData, error) {
|
||||
// *.crt files mounted to the HA TLS dir keyDirHA will be processed as trusted in the CMS keystore
|
||||
return configureTLSKeystores(keystoreDirHA, keyDirHA, keyDirHA, false)
|
||||
}
|
||||
|
||||
// ConfigureTLS configures TLS for the queue manager
|
||||
func ConfigureTLS(keyLabel string, cmsKeystore KeyStoreData, devMode bool, log *logger.Logger) error {
|
||||
|
||||
const mqsc string = "/etc/mqm/15-tls.mqsc"
|
||||
const mqscTemplate string = mqsc + ".tpl"
|
||||
|
||||
err := mqtemplate.ProcessTemplateFile(mqscTemplate, mqsc, map[string]string{
|
||||
"SSLKeyR": strings.TrimSuffix(cmsKeystore.Keystore.Filename, ".kdb"),
|
||||
"CertificateLabel": keyLabel,
|
||||
}, log)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if devMode && keyLabel != "" {
|
||||
err = configureTLSDev(log)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// configureTLSDev configures TLS for the developer defaults
|
||||
func configureTLSDev(log *logger.Logger) error {
|
||||
|
||||
const mqsc string = "/etc/mqm/20-dev-tls.mqsc"
|
||||
const mqscTemplate string = mqsc + ".tpl"
|
||||
|
||||
if os.Getenv("MQ_DEV") == "true" {
|
||||
err := mqtemplate.ProcessTemplateFile(mqscTemplate, mqsc, map[string]string{}, log)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
_, err := os.Stat(mqsc)
|
||||
if !os.IsNotExist(err) {
|
||||
err = os.Remove(mqsc)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to remove file %s: %v", mqsc, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// generateAllKeystores creates the CMS Keystore & PKCS#12 Truststore (if required)
|
||||
func generateAllKeystores(keystoreDir string, p12TruststoreRequired bool) (TLSStore, error) {
|
||||
|
||||
var cmsKeystore, p12Truststore KeyStoreData
|
||||
|
||||
// Generate a pasword for use with both the CMS Keystore & PKCS#12 Truststore
|
||||
pw := generateRandomPassword()
|
||||
cmsKeystore.Password = pw
|
||||
p12Truststore.Password = pw
|
||||
|
||||
// Create the Keystore directory - if it does not already exist
|
||||
// #nosec G301 - write group permissions are required
|
||||
err := os.MkdirAll(keystoreDir, 0770)
|
||||
if err != nil {
|
||||
return TLSStore{cmsKeystore, p12Truststore}, fmt.Errorf("Failed to create Keystore directory: %v", err)
|
||||
}
|
||||
|
||||
// Create the CMS Keystore
|
||||
cmsKeystore.Keystore = keystore.NewCMSKeyStore(filepath.Join(keystoreDir, cmsKeystoreName), cmsKeystore.Password)
|
||||
err = cmsKeystore.Keystore.Create()
|
||||
if err != nil {
|
||||
return TLSStore{cmsKeystore, p12Truststore}, fmt.Errorf("Failed to create CMS Keystore: %v", err)
|
||||
}
|
||||
|
||||
// Create the PKCS#12 Truststore (if required)
|
||||
if p12TruststoreRequired {
|
||||
p12Truststore.Keystore = keystore.NewPKCS12KeyStore(filepath.Join(keystoreDir, p12TruststoreName), p12Truststore.Password)
|
||||
err = p12Truststore.Keystore.Create()
|
||||
if err != nil {
|
||||
return TLSStore{cmsKeystore, p12Truststore}, fmt.Errorf("Failed to create PKCS#12 Truststore: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
return TLSStore{cmsKeystore, p12Truststore}, nil
|
||||
}
|
||||
|
||||
// processKeys processes all keys - adding them to the CMS KeyStore
|
||||
func processKeys(tlsStore *TLSStore, keystoreDir string, keyDir string) (string, error) {
|
||||
|
||||
// Key label - will be set to the label of the first set of keys
|
||||
keyLabel := ""
|
||||
|
||||
// Process all keys
|
||||
keyList, err := ioutil.ReadDir(keyDir)
|
||||
if err == nil && len(keyList) > 0 {
|
||||
|
||||
// Process each set of keys - each set should contain files: *.key & *.crt
|
||||
for _, keySet := range keyList {
|
||||
keys, _ := ioutil.ReadDir(filepath.Join(keyDir, keySet.Name()))
|
||||
|
||||
// Ensure the label of the set of keys does not match the name of the PKCS#12 Truststore
|
||||
if keySet.Name() == p12TruststoreName[0:len(p12TruststoreName)-len(filepath.Ext(p12TruststoreName))] {
|
||||
return "", fmt.Errorf("Key label cannot be set to the Truststore name: %v", keySet.Name())
|
||||
}
|
||||
|
||||
// Process private key (*.key)
|
||||
privateKey, keyPrefix, err := processPrivateKey(keyDir, keySet.Name(), keys)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// If private key does not exist - skip this set of keys
|
||||
if privateKey == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// Process certificates (*.crt) - public certificate & optional CA certificate
|
||||
publicCertificate, caCertificate, err := processCertificates(keyDir, keySet.Name(), keyPrefix, keys, &tlsStore.Keystore, &tlsStore.Truststore)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Create a new PKCS#12 Keystore - containing private key, public certificate & optional CA certificate
|
||||
file, err := pkcs.Encode(rand.Reader, privateKey, publicCertificate, caCertificate, tlsStore.Keystore.Password)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("Failed to encode PKCS#12 Keystore %s: %v", keySet.Name()+".p12", err)
|
||||
}
|
||||
// #nosec G306 - this gives permissions to owner/s group only.
|
||||
err = ioutil.WriteFile(filepath.Join(keystoreDir, keySet.Name()+".p12"), file, 0644)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("Failed to write PKCS#12 Keystore %s: %v", filepath.Join(keystoreDir, keySet.Name()+".p12"), err)
|
||||
}
|
||||
|
||||
// Import the new PKCS#12 Keystore into the CMS Keystore
|
||||
err = tlsStore.Keystore.Keystore.Import(filepath.Join(keystoreDir, keySet.Name()+".p12"), tlsStore.Keystore.Password)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("Failed to import keys from %s into CMS Keystore: %v", filepath.Join(keystoreDir, keySet.Name()+".p12"), err)
|
||||
}
|
||||
|
||||
// Relabel the certificate in the CMS Keystore
|
||||
err = relabelCertificate(keySet.Name(), &tlsStore.Keystore)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Set key label - for first set of keys only
|
||||
if keyLabel == "" {
|
||||
keyLabel = keySet.Name()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return keyLabel, nil
|
||||
}
|
||||
|
||||
// processTrustCertificates processes all trust certificates - adding them to the CMS KeyStore & PKCS#12 Truststore (if required)
|
||||
func processTrustCertificates(tlsStore *TLSStore, trustDir string) error {
|
||||
|
||||
// Process all trust certiifcates
|
||||
trustList, err := ioutil.ReadDir(trustDir)
|
||||
if err == nil && len(trustList) > 0 {
|
||||
|
||||
// Process each set of keys
|
||||
for _, trustSet := range trustList {
|
||||
keys, _ := ioutil.ReadDir(filepath.Join(trustDir, trustSet.Name()))
|
||||
|
||||
for _, key := range keys {
|
||||
if strings.HasSuffix(key.Name(), ".crt") {
|
||||
// #nosec G304 - filename variable is derived from contents of 'trustDir' which is a defined constant
|
||||
file, err := ioutil.ReadFile(filepath.Join(trustDir, trustSet.Name(), key.Name()))
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to read file %s: %v", filepath.Join(trustDir, trustSet.Name(), key.Name()), err)
|
||||
}
|
||||
|
||||
for string(file) != "" {
|
||||
var block *pem.Block
|
||||
block, file = pem.Decode(file)
|
||||
if block == nil {
|
||||
break
|
||||
}
|
||||
|
||||
// Add to known certificates for the CMS Keystore
|
||||
err = addToKnownCertificates(block, &tlsStore.Keystore, true)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to add to know certificates for CMS Keystore")
|
||||
}
|
||||
|
||||
if tlsStore.Truststore.Keystore != nil {
|
||||
// Add to known certificates for the PKCS#12 Truststore
|
||||
err = addToKnownCertificates(block, &tlsStore.Truststore, true)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to add to know certificates for PKCS#12 Truststore")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add all trust certificates to PKCS#12 Truststore (if required)
|
||||
if tlsStore.Truststore.Keystore != nil && len(tlsStore.Truststore.TrustedCerts) > 0 {
|
||||
err = addCertificatesToTruststore(&tlsStore.Truststore)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Add all trust certificates to CMS Keystore
|
||||
if len(tlsStore.Keystore.TrustedCerts) > 0 {
|
||||
err = addCertificatesToCMSKeystore(&tlsStore.Keystore)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// processPrivateKey processes the private key (*.key) from a set of keys
|
||||
func processPrivateKey(keyDir string, keySetName string, keys []os.FileInfo) (interface{}, string, error) {
|
||||
|
||||
var privateKey interface{}
|
||||
keyPrefix := ""
|
||||
|
||||
for _, key := range keys {
|
||||
|
||||
if strings.HasSuffix(key.Name(), ".key") {
|
||||
// #nosec G304 - filename variable is derived from contents of 'keyDir' which is a defined constant
|
||||
file, err := ioutil.ReadFile(filepath.Join(keyDir, keySetName, key.Name()))
|
||||
if err != nil {
|
||||
return nil, "", fmt.Errorf("Failed to read private key %s: %v", filepath.Join(keyDir, keySetName, key.Name()), err)
|
||||
}
|
||||
block, _ := pem.Decode(file)
|
||||
if block == nil {
|
||||
return nil, "", fmt.Errorf("Failed to decode private key %s: pem.Decode returned nil", filepath.Join(keyDir, keySetName, key.Name()))
|
||||
}
|
||||
|
||||
// Check if the private key is PKCS1
|
||||
privateKey, err = x509.ParsePKCS1PrivateKey(block.Bytes)
|
||||
if err != nil {
|
||||
// Check if the private key is PKCS8
|
||||
privateKey, err = x509.ParsePKCS8PrivateKey(block.Bytes)
|
||||
if err != nil {
|
||||
return nil, "", fmt.Errorf("Failed to parse private key %s: %v", filepath.Join(keyDir, keySetName, key.Name()), err)
|
||||
}
|
||||
}
|
||||
keyPrefix = key.Name()[0 : len(key.Name())-len(filepath.Ext(key.Name()))]
|
||||
}
|
||||
}
|
||||
|
||||
return privateKey, keyPrefix, nil
|
||||
}
|
||||
|
||||
// processCertificates processes the certificates (*.crt) from a set of keys
|
||||
func processCertificates(keyDir string, keySetName, keyPrefix string, keys []os.FileInfo, cmsKeystore, p12Truststore *KeyStoreData) (*x509.Certificate, []*x509.Certificate, error) {
|
||||
|
||||
var publicCertificate *x509.Certificate
|
||||
var caCertificate []*x509.Certificate
|
||||
|
||||
for _, key := range keys {
|
||||
|
||||
if strings.HasPrefix(key.Name(), keyPrefix) && strings.HasSuffix(key.Name(), ".crt") {
|
||||
// #nosec G304 - filename variable is derived from contents of 'keyDir' which is a defined constant
|
||||
file, err := ioutil.ReadFile(filepath.Join(keyDir, keySetName, key.Name()))
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("Failed to read public certificate %s: %v", filepath.Join(keyDir, keySetName, key.Name()), err)
|
||||
}
|
||||
block, _ := pem.Decode(file)
|
||||
if block == nil {
|
||||
return nil, nil, fmt.Errorf("Failed to decode public certificate %s: pem.Decode returned nil", filepath.Join(keyDir, keySetName, key.Name()))
|
||||
}
|
||||
publicCertificate, err = x509.ParseCertificate(block.Bytes)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("Failed to parse public certificate %s: %v", filepath.Join(keyDir, keySetName, key.Name()), err)
|
||||
}
|
||||
|
||||
// Add to known certificates for the CMS Keystore
|
||||
err = addToKnownCertificates(block, cmsKeystore, false)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("Failed to add to know certificates for CMS Keystore")
|
||||
}
|
||||
|
||||
} else if strings.HasSuffix(key.Name(), ".crt") {
|
||||
// #nosec G304 - filename variable is derived from contents of 'keyDir' which is a defined constant
|
||||
file, err := ioutil.ReadFile(filepath.Join(keyDir, keySetName, key.Name()))
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("Failed to read CA certificate %s: %v", filepath.Join(keyDir, keySetName, key.Name()), err)
|
||||
}
|
||||
|
||||
for string(file) != "" {
|
||||
var block *pem.Block
|
||||
block, file = pem.Decode(file)
|
||||
if block == nil {
|
||||
break
|
||||
}
|
||||
|
||||
// Add to known certificates for the CMS Keystore
|
||||
err = addToKnownCertificates(block, cmsKeystore, false)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("Failed to add to know certificates for CMS Keystore")
|
||||
}
|
||||
|
||||
if p12Truststore.Keystore != nil {
|
||||
// Add to known certificates for the PKCS#12 Truststore
|
||||
err = addToKnownCertificates(block, p12Truststore, true)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("Failed to add to know certificates for PKCS#12 Truststore")
|
||||
}
|
||||
}
|
||||
|
||||
certificate, err := x509.ParseCertificate(block.Bytes)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("Failed to parse CA certificate %s: %v", filepath.Join(keyDir, keySetName, key.Name()), err)
|
||||
}
|
||||
caCertificate = append(caCertificate, certificate)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return publicCertificate, caCertificate, nil
|
||||
}
|
||||
|
||||
// relabelCertificate sets a new label for a certificate in the CMS Keystore
|
||||
func relabelCertificate(newLabel string, cmsKeystore *KeyStoreData) error {
|
||||
|
||||
allLabels, err := cmsKeystore.Keystore.GetCertificateLabels()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to get list of all certificate labels from CMS Keystore: %v", err)
|
||||
}
|
||||
relabelled := false
|
||||
for _, label := range allLabels {
|
||||
found := false
|
||||
for _, keyLabel := range cmsKeystore.KeyLabels {
|
||||
if strings.Trim(label, "\"") == keyLabel {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
err = cmsKeystore.Keystore.RenameCertificate(strings.Trim(label, "\""), newLabel)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
relabelled = true
|
||||
cmsKeystore.KeyLabels = append(cmsKeystore.KeyLabels, newLabel)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !relabelled {
|
||||
return fmt.Errorf("Failed to relabel certificate for %s in CMS keystore", newLabel)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// addCertificatesToTruststore adds trust certificates to the PKCS#12 Truststore
|
||||
func addCertificatesToTruststore(p12Truststore *KeyStoreData) error {
|
||||
|
||||
temporaryPemFile := filepath.Join("/tmp", "trust.pem")
|
||||
_, err := os.Stat(temporaryPemFile)
|
||||
if err == nil {
|
||||
err = os.Remove(temporaryPemFile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to remove file %v: %v", temporaryPemFile, err)
|
||||
}
|
||||
}
|
||||
|
||||
err = writeCertificatesToFile(temporaryPemFile, p12Truststore.TrustedCerts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = p12Truststore.Keystore.AddNoLabel(temporaryPemFile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to add certificates to PKCS#12 Truststore: %v", err)
|
||||
}
|
||||
|
||||
// Relabel all certiifcates
|
||||
allCertificates, err := p12Truststore.Keystore.ListAllCertificates()
|
||||
if err != nil || len(allCertificates) <= 0 {
|
||||
return fmt.Errorf("Failed to get any certificates from PKCS#12 Truststore: %v", err)
|
||||
}
|
||||
|
||||
for i, certificate := range allCertificates {
|
||||
certificate = strings.Trim(certificate, "\"")
|
||||
certificate = strings.TrimSpace(certificate)
|
||||
newLabel := fmt.Sprintf("Trust%d", i)
|
||||
|
||||
err = p12Truststore.Keystore.RenameCertificate(certificate, newLabel)
|
||||
if err != nil || len(allCertificates) <= 0 {
|
||||
return fmt.Errorf("Failed to rename certificate %s to %s in PKCS#12 Truststore: %v", certificate, newLabel, err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// addCertificatesToCMSKeystore adds trust certificates to the CMS keystore
|
||||
func addCertificatesToCMSKeystore(cmsKeystore *KeyStoreData) error {
|
||||
|
||||
temporaryPemFile := filepath.Join("/tmp", "cmsTrust.pem")
|
||||
_, err := os.Stat(temporaryPemFile)
|
||||
if err == nil {
|
||||
err = os.Remove(temporaryPemFile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to remove file %v: %v", temporaryPemFile, err)
|
||||
}
|
||||
}
|
||||
|
||||
err = writeCertificatesToFile(temporaryPemFile, cmsKeystore.TrustedCerts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = cmsKeystore.Keystore.AddNoLabel(temporaryPemFile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to add certificates to CMS keystore: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// generateRandomPassword generates a random 12 character password from the characters a-z, A-Z, 0-9
|
||||
func generateRandomPassword() string {
|
||||
pwr.Seed(time.Now().Unix())
|
||||
validChars := "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
|
||||
validcharArray := []byte(validChars)
|
||||
password := ""
|
||||
for i := 0; i < 12; i++ {
|
||||
// #nosec G404 - this is only for internal keystore and using math/rand pose no harm.
|
||||
password = password + string(validcharArray[pwr.Intn(len(validcharArray))])
|
||||
}
|
||||
|
||||
return password
|
||||
}
|
||||
|
||||
// addToKnownCertificates adds to the list of known certificates for a Keystore
|
||||
func addToKnownCertificates(block *pem.Block, keyData *KeyStoreData, addToKeystore bool) error {
|
||||
sha512str, err := getCertificateFingerprint(block)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
known := false
|
||||
for _, fingerprint := range keyData.KnownFingerPrints {
|
||||
if fingerprint == sha512str {
|
||||
known = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !known {
|
||||
if addToKeystore {
|
||||
keyData.TrustedCerts = append(keyData.TrustedCerts, block)
|
||||
}
|
||||
keyData.KnownFingerPrints = append(keyData.KnownFingerPrints, sha512str)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// getCertificateFingerprint returns a fingerprint for a certificate
|
||||
func getCertificateFingerprint(block *pem.Block) (string, error) {
|
||||
certificate, err := x509.ParseCertificate(block.Bytes)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("Failed to parse x509 certificate: %v", err)
|
||||
}
|
||||
sha512Sum := sha512.Sum512(certificate.Raw)
|
||||
sha512str := string(sha512Sum[:])
|
||||
|
||||
return sha512str, nil
|
||||
}
|
||||
|
||||
// writeCertificatesToFile writes a list of certificates to a file
|
||||
func writeCertificatesToFile(file string, certificates []*pem.Block) error {
|
||||
|
||||
// #nosec G304 - this is a temporary pem file to write certs.
|
||||
f, err := os.Create(file)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to create file %s: %v", file, err)
|
||||
}
|
||||
// #nosec G307 - local to this function, pose no harm.
|
||||
defer f.Close()
|
||||
|
||||
w := bufio.NewWriter(f)
|
||||
|
||||
for i, c := range certificates {
|
||||
err := pem.Encode(w, c)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to encode certificate number %d: %v", i, err)
|
||||
}
|
||||
err = w.Flush()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to write certificate to file %s: %v", file, err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
97
internal/tls/tls_web.go
Normal file
97
internal/tls/tls_web.go
Normal file
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
© Copyright IBM Corporation 2019, 2021
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
package tls
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/ibm-messaging/mq-container/internal/keystore"
|
||||
)
|
||||
|
||||
// webKeystoreDefault is the name of the default web server Keystore
|
||||
const webKeystoreDefault = "default.p12"
|
||||
|
||||
// ConfigureWebTLS configures TLS for the web server
|
||||
func ConfigureWebTLS(keyLabel string) error {
|
||||
|
||||
// Return immediately if we have no certificate to use as identity
|
||||
if keyLabel == "" && os.Getenv("MQ_GENERATE_CERTIFICATE_HOSTNAME") == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
webConfigDir := "/etc/mqm/web/installations/Installation1/servers/mqweb"
|
||||
tls := "tls.xml"
|
||||
|
||||
tlsConfig := filepath.Join(webConfigDir, tls)
|
||||
newTLSConfig := filepath.Join(webConfigDir, tls+".tpl")
|
||||
|
||||
err := os.Remove(tlsConfig)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to delete file %s: %v", tlsConfig, err)
|
||||
}
|
||||
|
||||
// Symlink here to prevent issues on restart
|
||||
err = os.Symlink(newTLSConfig, tlsConfig)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to create symlink %s->%s: %v", newTLSConfig, tlsConfig, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ConfigureWebKeyStore configures the Web Keystore
|
||||
func ConfigureWebKeystore(p12Truststore KeyStoreData, webKeystore string) (string, error) {
|
||||
|
||||
if webKeystore == "" {
|
||||
webKeystore = webKeystoreDefault
|
||||
}
|
||||
webKeystoreFile := filepath.Join(keystoreDirDefault, webKeystore)
|
||||
|
||||
// Check if a new self-signed certificate should be generated
|
||||
genHostName := os.Getenv("MQ_GENERATE_CERTIFICATE_HOSTNAME")
|
||||
if genHostName != "" {
|
||||
|
||||
// Create the Web Keystore
|
||||
newWebKeystore := keystore.NewPKCS12KeyStore(webKeystoreFile, p12Truststore.Password)
|
||||
err := newWebKeystore.Create()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("Failed to create Web Keystore %s: %v", webKeystoreFile, err)
|
||||
}
|
||||
|
||||
// Generate a new self-signed certificate in the Web Keystore
|
||||
err = newWebKeystore.CreateSelfSignedCertificate("default", fmt.Sprintf("CN=%s", genHostName), genHostName)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("Failed to generate certificate in Web Keystore %s with DN of 'CN=%s': %v", webKeystoreFile, genHostName, err)
|
||||
}
|
||||
|
||||
} else {
|
||||
// Check Web Keystore already exists
|
||||
_, err := os.Stat(webKeystoreFile)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("Failed to find existing Web Keystore %s: %v", webKeystoreFile, err)
|
||||
}
|
||||
}
|
||||
|
||||
// Check Web Truststore already exists
|
||||
_, err := os.Stat(p12Truststore.Keystore.Filename)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("Failed to find existing Web Truststore %s: %v", p12Truststore.Keystore.Filename, err)
|
||||
}
|
||||
|
||||
return webKeystore, nil
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
© Copyright IBM Corporation 2018, 2019
|
||||
© Copyright IBM Corporation 2018, 2020
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -16,66 +16,26 @@ limitations under the License.
|
||||
package user
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os/user"
|
||||
"strings"
|
||||
|
||||
"github.com/ibm-messaging/mq-container/internal/command"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// User holds information on primary and supplemental OS groups
|
||||
type User struct {
|
||||
UID string
|
||||
Name string
|
||||
PrimaryGID string
|
||||
SupplementalGID []string
|
||||
UID int
|
||||
PrimaryGID int
|
||||
SupplementalGID []int
|
||||
}
|
||||
|
||||
// GetUser returns the current user and group information
|
||||
func GetUser() (User, error) {
|
||||
u, err := user.Current()
|
||||
u := User{
|
||||
UID: unix.Geteuid(),
|
||||
PrimaryGID: unix.Getgid(),
|
||||
}
|
||||
groups, err := unix.Getgroups()
|
||||
if err != nil {
|
||||
return User{}, err
|
||||
return u, err
|
||||
}
|
||||
g, err := getCurrentUserGroups()
|
||||
if err != nil {
|
||||
return User{}, err
|
||||
}
|
||||
if err != nil && len(g) == 0 {
|
||||
return User{
|
||||
UID: u.Uid,
|
||||
Name: u.Name,
|
||||
PrimaryGID: u.Gid,
|
||||
SupplementalGID: []string{},
|
||||
}, nil
|
||||
}
|
||||
// Look for the primary group in the list of group IDs
|
||||
for i, v := range g {
|
||||
if v == u.Gid {
|
||||
// Remove the element from the slice
|
||||
g = append(g[:i], g[i+1:]...)
|
||||
}
|
||||
}
|
||||
return User{
|
||||
UID: u.Uid,
|
||||
Name: u.Name,
|
||||
PrimaryGID: u.Gid,
|
||||
SupplementalGID: g,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func getCurrentUserGroups() ([]string, error) {
|
||||
var nilArray []string
|
||||
out, _, err := command.Run("id", "--groups")
|
||||
if err != nil {
|
||||
return nilArray, err
|
||||
}
|
||||
|
||||
out = strings.TrimSpace(out)
|
||||
if out == "" {
|
||||
return nilArray, fmt.Errorf("Unable to determine groups for current user")
|
||||
}
|
||||
|
||||
groups := strings.Split(out, " ")
|
||||
return groups, nil
|
||||
u.SupplementalGID = groups
|
||||
return u, nil
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
Fat manifests
|
||||
=============
|
||||
|
||||
These are the fat manifests used by Docker Hub and Docker store to handle images with multiple CPU architectures.
|
||||
These are the fat manifests used by Docker Hub to handle images with multiple CPU architectures.
|
||||
|
||||
They are used in conjunction with [manifest-tool](https://github.com/estesp/manifest-tool), for example:
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# © Copyright IBM Corporation 2018
|
||||
# © Copyright IBM Corporation 2019
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
@@ -12,18 +12,17 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
image: ibmcorp/mqadvanced-server-dev:9.1.0.0
|
||||
image: ibmcom/mq:9.1.2.0-UBI
|
||||
manifests:
|
||||
- image: ibmcorp/mqadvanced-server-dev:9.1.0.0-x86_64
|
||||
- image: ibmcom/mq:9.1.2.0-UBI-amd64
|
||||
platform:
|
||||
architecture: amd64
|
||||
os: linux
|
||||
- image: ibmcorp/mqadvanced-server-dev:9.1.0.0-ppc64le
|
||||
- image: ibmcom/mq:9.1.2.0-UBI-ppc64le
|
||||
platform:
|
||||
architecture: ppc64le
|
||||
os: linux
|
||||
- image: ibmcorp/mqadvanced-server-dev:9.1.0.0-s390x
|
||||
- image: ibmcom/mq:9.1.2.0-UBI-s390x
|
||||
platform:
|
||||
architecture: s390x
|
||||
os: linux
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# © Copyright IBM Corporation 2018
|
||||
# © Copyright IBM Corporation 2019
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
@@ -12,18 +12,17 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
image: ibmcorp/mqadvanced-server-dev:9.1.1.0
|
||||
image: ibmcom/mq:9.1.3.0-r3
|
||||
manifests:
|
||||
- image: ibmcorp/mqadvanced-server-dev:9.1.1.0-x86_64
|
||||
- image: ibmcom/mq:9.1.3.0-r3-amd64
|
||||
platform:
|
||||
architecture: amd64
|
||||
os: linux
|
||||
- image: ibmcorp/mqadvanced-server-dev:9.1.1.0-ppc64le
|
||||
- image: ibmcom/mq:9.1.3.0-r3-ppc64le
|
||||
platform:
|
||||
architecture: ppc64le
|
||||
os: linux
|
||||
- image: ibmcorp/mqadvanced-server-dev:9.1.1.0-s390x
|
||||
- image: ibmcom/mq:9.1.3.0-r3-s390x
|
||||
platform:
|
||||
architecture: s390x
|
||||
os: linux
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# © Copyright IBM Corporation 2018, 2019
|
||||
# © Copyright IBM Corporation 2019
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
@@ -12,18 +12,17 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
image: ibmcorp/mqadvanced-server-dev:9.1.2.0
|
||||
image: ibmcom/mq:9.1.4.0-r1
|
||||
manifests:
|
||||
- image: ibmcorp/mqadvanced-server-dev:9.1.2.0-x86_64
|
||||
- image: ibmcom/mq:9.1.4.0-r1-amd64
|
||||
platform:
|
||||
architecture: amd64
|
||||
os: linux
|
||||
- image: ibmcorp/mqadvanced-server-dev:9.1.2.0-ppc64le
|
||||
- image: ibmcom/mq:9.1.4.0-r1-ppc64le
|
||||
platform:
|
||||
architecture: ppc64le
|
||||
os: linux
|
||||
- image: ibmcorp/mqadvanced-server-dev:9.1.2.0-s390x
|
||||
- image: ibmcom/mq:9.1.4.0-r1-s390x
|
||||
platform:
|
||||
architecture: s390x
|
||||
os: linux
|
||||
|
||||
28
manifests/dockerhub/manifest-9.1.5.yaml
Normal file
28
manifests/dockerhub/manifest-9.1.5.yaml
Normal file
@@ -0,0 +1,28 @@
|
||||
# © Copyright IBM Corporation 2020
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
image: ibmcom/mq:9.1.5.0-r1
|
||||
manifests:
|
||||
- image: ibmcom/mq:9.1.5.0-r1-amd64
|
||||
platform:
|
||||
architecture: amd64
|
||||
os: linux
|
||||
- image: ibmcom/mq:9.1.5.0-r1-ppc64le
|
||||
platform:
|
||||
architecture: ppc64le
|
||||
os: linux
|
||||
- image: ibmcom/mq:9.1.5.0-r1-s390x
|
||||
platform:
|
||||
architecture: s390x
|
||||
os: linux
|
||||
@@ -1,4 +1,4 @@
|
||||
# © Copyright IBM Corporation 2017, 2018
|
||||
# © Copyright IBM Corporation 2018, 2020
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
@@ -12,13 +12,9 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/docker/docker"
|
||||
version = "=v17.03.2-ce"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/docker/go-connections"
|
||||
version = "0.4.0"
|
||||
|
||||
[prune]
|
||||
go-tests = true
|
||||
image: ibmcom/mq:9.2.0.0-r1
|
||||
manifests:
|
||||
- image: ibmcom/mq:9.2.0.0-r1-amd64
|
||||
platform:
|
||||
architecture: amd64
|
||||
os: linux
|
||||
@@ -1,4 +1,4 @@
|
||||
# © Copyright IBM Corporation 2018, 2019
|
||||
# © Copyright IBM Corporation 2018, 2020
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
@@ -14,16 +14,7 @@
|
||||
|
||||
image: ibmcom/mq:latest
|
||||
manifests:
|
||||
- image: ibmcom/mq:9.1.2.0-x86_64
|
||||
- image: ibmcom/mq:9.2.0.0-r1-amd64
|
||||
platform:
|
||||
architecture: amd64
|
||||
os: linux
|
||||
- image: ibmcom/mq:9.1.2.0-ppc64le
|
||||
platform:
|
||||
architecture: ppc64le
|
||||
os: linux
|
||||
- image: ibmcom/mq:9.1.2.0-s390x
|
||||
platform:
|
||||
architecture: s390x
|
||||
os: linux
|
||||
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
# RHEL-based container build
|
||||
|
||||
Build scripts for building a container image based on Red Hat Enterprise Linux (RHEL), using the [`buildah`](https://github.com/containers/buildah) tool. buildah is supported on RHEL V7.5 and greater.
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user